mirror of
https://github.com/strapi/strapi.git
synced 2025-07-13 12:02:10 +00:00
282 lines
7.2 KiB
JavaScript
282 lines
7.2 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Module dependencies
|
|
*/
|
|
|
|
// Public node modules.
|
|
const _ = require('lodash');
|
|
|
|
// Local Strapi dependencies.
|
|
const utils = require('../utils/utils');
|
|
|
|
/**
|
|
* JSON API helper
|
|
*/
|
|
|
|
module.exports = {
|
|
|
|
default: {},
|
|
|
|
/**
|
|
* Parse request
|
|
*/
|
|
|
|
parse: function * (ctx) {
|
|
// Assign used method
|
|
this.default.method = ctx.method.toUpperCase();
|
|
|
|
// Detect X-HTTP-Method-Override for some clients
|
|
// which don't support PATCH method
|
|
if (ctx.header.hasOwnProperty('x-http-method-override') && ctx.header['x-http-method-override'] === 'PATCH') {
|
|
this.default.method = 'POST';
|
|
}
|
|
|
|
// HTTP methods allowed
|
|
switch (this.default.method) {
|
|
case 'GET':
|
|
try {
|
|
yield this.fetchQuery(ctx);
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
break;
|
|
case 'PATCH':
|
|
case 'POST':
|
|
try {
|
|
if (utils.getObject(utils.matchedRoute(ctx)) === 'relationships') {
|
|
yield this.fetchRelationRequest(ctx);
|
|
yield this.formatRelationBody(ctx);
|
|
} else {
|
|
yield this.fetchSchema(ctx);
|
|
yield this.formatBody(ctx);
|
|
}
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
break;
|
|
case 'DELETE':
|
|
if (utils.getObject(utils.matchedRoute(ctx)) === 'relationships') {
|
|
try {
|
|
yield this.fetchRelationRequest(ctx);
|
|
yield this.formatRelationBody(ctx);
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Invalid HTTP method'
|
|
}
|
|
};
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Format attributes for relationships request to get more simple API
|
|
*/
|
|
|
|
formatRelationBody: function * (ctx) {
|
|
ctx.request.body = _.map(ctx.request.body.data, 'id');
|
|
},
|
|
|
|
/**
|
|
* Fetch request for relationships
|
|
*/
|
|
|
|
fetchRelationRequest: function * (ctx) {
|
|
const body = ctx.request.body;
|
|
|
|
if (!body.hasOwnProperty('data')) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Missing `data` member'
|
|
}
|
|
};
|
|
} else if (_.isPlainObject(body.data) && !utils.isRessourceObject(body.data)) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Invalid ressource object'
|
|
}
|
|
};
|
|
} else if (_.isArray(body.data)) {
|
|
const errors = _.remove(_.map(body.data, function (n, position) {
|
|
if (!utils.isRessourceObject(n) || _.isUndefined(utils.getType(ctx, _.get(n, 'type')))) {
|
|
return {
|
|
position: position,
|
|
message: 'Invalid ressource object or unknow type'
|
|
};
|
|
}
|
|
}), function (n) {
|
|
return !_.isUndefined(n);
|
|
});
|
|
|
|
if (!_.isEmpty(errors)) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: errors
|
|
}
|
|
};
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Format attributes for more simple API
|
|
*/
|
|
|
|
formatBody: function * (ctx) {
|
|
const body = ctx.request.body;
|
|
const values = _.assign({}, body.data.attributes, _.mapValues(body.data.relationships, function (relation) {
|
|
return _.isArray(relation.data) ? _.map(relation.data, 'id') : relation.data.id;
|
|
}));
|
|
|
|
ctx.request.body = values;
|
|
},
|
|
|
|
/**
|
|
* Fetch query parameters
|
|
*/
|
|
|
|
fetchQuery: function * (ctx) {
|
|
// Use context namespace for passing information throug middleware
|
|
ctx.state.filter = {
|
|
fields: {}
|
|
};
|
|
|
|
_.forEach(ctx.query, function (value, key) {
|
|
if (_.includes(['include', 'sort', 'page', 'filter'], key)) {
|
|
throw {
|
|
status: 400,
|
|
body: {
|
|
message: 'Parameter `' + key + '` is not supported yet'
|
|
}
|
|
};
|
|
} else if (key.indexOf('fields') !== -1) {
|
|
const alias = key.match(/\[(.*?)\]/)[1];
|
|
const type = utils.getType(ctx, alias);
|
|
|
|
if (_.isUndefined(type)) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Wrong `type` in fields parameters'
|
|
}
|
|
};
|
|
}
|
|
|
|
ctx.state.filter.fields[type] = value.split(',');
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Fetch attributes schema
|
|
*/
|
|
|
|
fetchSchema: function * (ctx) {
|
|
const body = ctx.request.body;
|
|
|
|
if (!body.hasOwnProperty('data')) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Missing `data` member'
|
|
}
|
|
};
|
|
} else if (!utils.isRessourceObject(body.data) && this.default.method !== 'POST') {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Invalid ressource object'
|
|
}
|
|
};
|
|
} else if (!body.data.hasOwnProperty('type') && this.default.method === 'POST') {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Invalid ressource object'
|
|
}
|
|
};
|
|
} else if (!strapi.models.hasOwnProperty(body.data.type)) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Unknow `type` ' + body.data.type
|
|
}
|
|
};
|
|
}
|
|
|
|
// Extract required attributes
|
|
const requiredAttributes = _.omit(_.mapValues(strapi.models[body.data.type].attributes, function (attr) {
|
|
return (attr.required && attr.type) ? attr : undefined;
|
|
}), _.isUndefined);
|
|
// Identify missing attributes
|
|
const missingAttributes = body.data.hasOwnProperty('attributes') ? _.difference(_.keys(requiredAttributes), _.keys(body.data.attributes)) : null;
|
|
|
|
if (!_.isEmpty(missingAttributes)) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Missing required attributes (' + missingAttributes.toString() + ')'
|
|
}
|
|
};
|
|
}
|
|
|
|
// Extract required relationships
|
|
const requiredRelationships = _.omit(_.mapValues(strapi.models[body.data.type].attributes, function (attr) {
|
|
return (attr.required && (attr.model || attr.collection)) ? attr : undefined;
|
|
}), _.isUndefined);
|
|
// Identify missing relationships
|
|
const missingRelationships = body.data.hasOwnProperty('relationships') ? _.difference(_.keys(requiredRelationships), _.keys(body.data.relationships)) : null;
|
|
|
|
if (!_.isEmpty(missingRelationships)) {
|
|
throw {
|
|
status: 403,
|
|
body: {
|
|
message: 'Missing required relationships (' + missingRelationships.toString() + ')'
|
|
}
|
|
};
|
|
}
|
|
|
|
// Build array of errors
|
|
if (_.size(requiredRelationships)) {
|
|
const errors = _.remove(_.flattenDeep(_.map(body.data.relationships, function (relation, key) {
|
|
if (!relation.hasOwnProperty('data')) {
|
|
return {
|
|
message: 'Missing `data` member for relationships ' + relation
|
|
};
|
|
} else if (_.isArray(relation.data)) {
|
|
return _.map(relation.data, function (ressource, position) {
|
|
if (!utils.isRessourceObject(ressource)) {
|
|
return {
|
|
position: position,
|
|
message: 'Invalid ressource object in relationships ' + key
|
|
};
|
|
}
|
|
});
|
|
} else if (!utils.isRessourceObject(relation.data)) {
|
|
return {
|
|
message: 'Invalid ressource object for relationships ' + key
|
|
};
|
|
}
|
|
})), function (n) {
|
|
return !_.isUndefined(n);
|
|
});
|
|
|
|
if (!_.isEmpty(errors)) {
|
|
throw {
|
|
status: 403,
|
|
body: errors
|
|
};
|
|
}
|
|
}
|
|
}
|
|
};
|