add meta/models controller to get public models paths

This commit is contained in:
Matteo Pagliazzi
2016-02-29 00:09:09 +01:00
parent 5e68589ac8
commit 3512233966
6 changed files with 101 additions and 14 deletions

View File

@@ -90,5 +90,5 @@
"noAdminAccess": "You don't have admin access.", "noAdminAccess": "You don't have admin access.",
"pageMustBeNumber": "req.query.page must be a number", "pageMustBeNumber": "req.query.page must be a number",
"missingUnsubscriptionCode": "Missing unsubscription code.", "missingUnsubscriptionCode": "Missing unsubscription code.",
"userNotFound": "User Not Found" "userNotFound": "User not Found"
} }

View File

@@ -0,0 +1,30 @@
import {
generateUser,
translate as t,
} from '../../../../../helpers/api-integration/v3';
describe('GET /meta/models/:model/paths', () => {
let user;
before(async () => {
user = await generateUser();
});
it('returns an error when model is not accessible or doesn\'t exists', async () => {
await expect(user.get('/meta/models/1234/paths')).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});
let models = ['habit', 'daily', 'todo', 'reward', 'user', 'tag', 'challenge', 'group'];
models.forEach(model => {
it(`returns the model paths for ${model}`, async () => {
let res = await user.get(`/meta/models/${model}/paths`);
expect(res._id).to.equal('String');
expect(res).to.not.have.keys('__v');
});
});
});

View File

@@ -8,7 +8,7 @@ import {
let api = {}; let api = {};
/** /**
* @api {post} /unsubscribe Unsubscribe an email or user from email notifications * @api {post} /email/unsubscribe Unsubscribe an email or user from email notifications
* @apiVersion 3.0.0 * @apiVersion 3.0.0
* @apiName UnsubscribeEmail * @apiName UnsubscribeEmail
* @apiGroup Unsubscribe * @apiGroup Unsubscribe

View File

@@ -0,0 +1,39 @@
import mongoose from 'mongoose';
let api = {};
let tasksModels = ['habit', 'daily', 'todo', 'reward'];
let allModels = ['user', 'tag', 'challenge', 'group'].concat(tasksModels);
/**
* @api {get} /meta/models/:model/paths Get all paths for the specified model. Doesn't require authentication
* @apiVersion 3.0.0
* @apiName GetUserModelPaths
* @apiGroup Meta
*
* @apiParam {string="user","group","challenge","tag","habit","daily","todo","reward"} model The name of the model
*
* @apiSuccess {object} paths A key-value object made of fieldPath: fieldType (like {'field.nested': Boolean})
*/
api.getModelPaths = {
method: 'GET',
url: '/meta/models/:model/paths',
async handler (req, res) {
req.checkParams('model', res.t('modelNotFound')).notEmpty().isIn(allModels);
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let model = req.params.model;
// tasks models are lowercase, the others have the first letter uppercase (User, Group)
if (tasksModels.indexOf(model) === -1) {
model = model.charAt(0).toUpperCase() + model.slice(1);
}
model = mongoose.model(model);
res.respond(200, model.getModelPaths());
},
};
export default api;

View File

@@ -1,6 +1,7 @@
import { uuid } from '../../../../common'; import { uuid } from '../../../../common';
import validator from 'validator'; import validator from 'validator';
import objectPath from 'object-path'; // TODO use lodash's unset once v4 is out import objectPath from 'object-path'; // TODO use lodash's unset once v4 is out
import _ from 'lodash';
export default function baseModel (schema, options = {}) { export default function baseModel (schema, options = {}) {
schema.add({ schema.add({
@@ -45,8 +46,9 @@ export default function baseModel (schema, options = {}) {
return options.sanitizeTransform ? options.sanitizeTransform(objToSanitize) : objToSanitize; return options.sanitizeTransform ? options.sanitizeTransform(objToSanitize) : objToSanitize;
}; };
if (!schema.options.toJSON) schema.options.toJSON = {};
if (Array.isArray(options.private)) privateFields.push(...options.private); if (Array.isArray(options.private)) privateFields.push(...options.private);
if (!schema.options.toJSON) schema.options.toJSON = {};
schema.options.toJSON.transform = function transformToObject (doc, plainObj) { schema.options.toJSON.transform = function transformToObject (doc, plainObj) {
privateFields.forEach((fieldPath) => { privateFields.forEach((fieldPath) => {
objectPath.del(plainObj, fieldPath); objectPath.del(plainObj, fieldPath);
@@ -55,4 +57,14 @@ export default function baseModel (schema, options = {}) {
// Allow an additional toJSON transform function to be used // Allow an additional toJSON transform function to be used
return options.toJSONTransform ? options.toJSONTransform(plainObj) : plainObj; return options.toJSONTransform ? options.toJSONTransform(plainObj) : plainObj;
}; };
schema.statics.getModelPaths = function getModelPaths () {
return _.reduce(this.schema.paths, (result, field, path) => {
if (privateFields.indexOf(path) === -1) {
result[path] = field.instance || 'Boolean';
}
return result;
}, {});
};
} }

View File

@@ -10,12 +10,14 @@ let router = express.Router(); // eslint-disable-line babel/new-cap
// It takes the async function, execute it and pass any error to next (args[2]) // It takes the async function, execute it and pass any error to next (args[2])
let _wrapAsyncFn = fn => (...args) => fn(...args).catch(args[2]); let _wrapAsyncFn = fn => (...args) => fn(...args).catch(args[2]);
fs function walkControllers (filePath) {
.readdirSync(CONTROLLERS_PATH) fs
.filter(fileName => fileName.match(/\.js$/)) .readdirSync(filePath)
.filter(fileName => fs.statSync(CONTROLLERS_PATH + fileName).isFile()) .forEach(fileName => {
.forEach((fileName) => { if (!fs.statSync(filePath + fileName).isFile()) {
let controller = require(CONTROLLERS_PATH + fileName); // eslint-disable-line global-require walkControllers(`${filePath}${fileName}/`);
} else if (fileName.match(/\.js$/)) {
let controller = require(filePath + fileName); // eslint-disable-line global-require
_.each(controller, (action) => { _.each(controller, (action) => {
let {method, url, middlewares = [], handler} = action; let {method, url, middlewares = [], handler} = action;
@@ -23,6 +25,10 @@ fs
method = method.toLowerCase(); method = method.toLowerCase();
router[method](url, ...middlewares, _wrapAsyncFn(handler)); router[method](url, ...middlewares, _wrapAsyncFn(handler));
}); });
}
}); });
}
walkControllers(CONTROLLERS_PATH);
export default router; export default router;