mirror of
https://github.com/strapi/strapi.git
synced 2025-11-23 05:30:11 +00:00
Use koa-body to handle multipart data
This commit is contained in:
parent
5228c0ef39
commit
63393f88a3
@ -19,11 +19,10 @@ const mixinAfter = require('./private/after');
|
|||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Private properties
|
// Private properties
|
||||||
let _instance = null;
|
let _instance = null;
|
||||||
|
|
||||||
class Strapi extends EventEmitter {
|
class Strapi extends EventEmitter {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
|||||||
@ -6,22 +6,13 @@
|
|||||||
|
|
||||||
module.exports = strapi => {
|
module.exports = strapi => {
|
||||||
return {
|
return {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default options
|
* Default options
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defaults: {
|
defaults: {
|
||||||
parser: {
|
parser: {
|
||||||
encode: 'utf-8',
|
multipart: true
|
||||||
formLimit: '56kb',
|
|
||||||
jsonLimit: '1mb',
|
|
||||||
strict: true,
|
|
||||||
extendTypes: {
|
|
||||||
json: [
|
|
||||||
'application/x-javascript'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -30,13 +21,11 @@ module.exports = strapi => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
initialize: cb => {
|
initialize: cb => {
|
||||||
strapi.app.use(strapi.middlewares.bodyparser({
|
strapi.app.use(
|
||||||
encode: strapi.config.parser.encode,
|
strapi.middlewares.convert(
|
||||||
formLimit: strapi.config.parser.formLimit,
|
strapi.middlewares.body(strapi.config.parser)
|
||||||
jsonLimit: strapi.config.parser.jsonLimit,
|
)
|
||||||
strict: strapi.config.parser.strict,
|
);
|
||||||
extendTypes: strapi.config.parser.extendTypes
|
|
||||||
}));
|
|
||||||
|
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,6 @@ const regex = require('strapi-utils').regex;
|
|||||||
|
|
||||||
module.exports = strapi => {
|
module.exports = strapi => {
|
||||||
return {
|
return {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default options
|
* Default options
|
||||||
*/
|
*/
|
||||||
@ -52,25 +51,39 @@ module.exports = strapi => {
|
|||||||
// and match the controller and action to the desired endpoint.
|
// and match the controller and action to the desired endpoint.
|
||||||
|
|
||||||
_.forEach(strapi.config.routes, value => {
|
_.forEach(strapi.config.routes, value => {
|
||||||
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
|
if (
|
||||||
|
_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpoint = `${value.method} ${value.path}`;
|
const endpoint = `${value.method} ${value.path}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {policies, action, validate} = routerChecker(value, endpoint);
|
const { policies, action, validate } = routerChecker(value, endpoint);
|
||||||
|
|
||||||
if (_.isUndefined(action) || !_.isFunction(action)) {
|
if (_.isUndefined(action) || !_.isFunction(action)) {
|
||||||
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
|
return strapi.log.warn(
|
||||||
|
'Ignored attempt to bind route `' +
|
||||||
|
endpoint +
|
||||||
|
'` to unknown controller/action.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
strapi.router.route(_.omitBy({
|
strapi.router.route(
|
||||||
|
_.omitBy(
|
||||||
|
{
|
||||||
method: value.method,
|
method: value.method,
|
||||||
path: value.path,
|
path: value.path,
|
||||||
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
|
handler: _.remove(
|
||||||
|
[strapi.middlewares.compose(policies), action],
|
||||||
|
o => _.isFunction(o)
|
||||||
|
),
|
||||||
validate
|
validate
|
||||||
}, _.isEmpty));
|
},
|
||||||
|
_.isEmpty
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
}
|
}
|
||||||
@ -81,31 +94,47 @@ module.exports = strapi => {
|
|||||||
const routerAdmin = strapi.middlewares.joiRouter();
|
const routerAdmin = strapi.middlewares.joiRouter();
|
||||||
|
|
||||||
_.forEach(strapi.admin.config.routes, value => {
|
_.forEach(strapi.admin.config.routes, value => {
|
||||||
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
|
if (
|
||||||
|
_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpoint = `${value.method} ${value.path}`;
|
const endpoint = `${value.method} ${value.path}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {policies, action, validate} = routerChecker(value, endpoint);
|
const { policies, action, validate } = routerChecker(value, endpoint);
|
||||||
|
|
||||||
if (_.isUndefined(action) || !_.isFunction(action)) {
|
if (_.isUndefined(action) || !_.isFunction(action)) {
|
||||||
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
|
return strapi.log.warn(
|
||||||
|
'Ignored attempt to bind route `' +
|
||||||
|
endpoint +
|
||||||
|
'` to unknown controller/action.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
routerAdmin.route(_.omitBy({
|
routerAdmin.route(
|
||||||
|
_.omitBy(
|
||||||
|
{
|
||||||
method: value.method,
|
method: value.method,
|
||||||
path: value.path,
|
path: value.path,
|
||||||
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
|
handler: _.remove(
|
||||||
|
[strapi.middlewares.compose(policies), action],
|
||||||
|
o => _.isFunction(o)
|
||||||
|
),
|
||||||
validate
|
validate
|
||||||
}, _.isEmpty));
|
},
|
||||||
|
_.isEmpty
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
routerAdmin.prefix(strapi.config.admin || `/${strapi.config.paths.admin}`);
|
routerAdmin.prefix(
|
||||||
|
strapi.config.admin || `/${strapi.config.paths.admin}`
|
||||||
|
);
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - Mount on main router `strapi.router.use(routerAdmin.middleware());`
|
// - Mount on main router `strapi.router.use(routerAdmin.middleware());`
|
||||||
@ -120,29 +149,50 @@ module.exports = strapi => {
|
|||||||
const router = strapi.middlewares.joiRouter();
|
const router = strapi.middlewares.joiRouter();
|
||||||
|
|
||||||
// Exclude routes with prefix.
|
// Exclude routes with prefix.
|
||||||
const excludedRoutes = _.omitBy(value, o => !o.hasOwnProperty('prefix'));
|
const excludedRoutes = _.omitBy(
|
||||||
|
value,
|
||||||
|
o => !o.hasOwnProperty('prefix')
|
||||||
|
);
|
||||||
|
|
||||||
// Add others routes to the plugin's router.
|
// Add others routes to the plugin's router.
|
||||||
_.forEach(_.omit(value, _.keys(excludedRoutes)), value => {
|
_.forEach(_.omit(value, _.keys(excludedRoutes)), value => {
|
||||||
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
|
if (
|
||||||
|
_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpoint = `${value.method} ${value.path}`;
|
const endpoint = `${value.method} ${value.path}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {policies, action, validate} = routerChecker(value, endpoint, plugin);
|
const { policies, action, validate } = routerChecker(
|
||||||
|
value,
|
||||||
|
endpoint,
|
||||||
|
plugin
|
||||||
|
);
|
||||||
|
|
||||||
if (_.isUndefined(action) || !_.isFunction(action)) {
|
if (_.isUndefined(action) || !_.isFunction(action)) {
|
||||||
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
|
return strapi.log.warn(
|
||||||
|
'Ignored attempt to bind route `' +
|
||||||
|
endpoint +
|
||||||
|
'` to unknown controller/action.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
router.route(_.omitBy({
|
router.route(
|
||||||
|
_.omitBy(
|
||||||
|
{
|
||||||
method: value.method,
|
method: value.method,
|
||||||
path: value.path,
|
path: value.path,
|
||||||
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
|
handler: _.remove(
|
||||||
|
[strapi.middlewares.compose(policies), action],
|
||||||
|
o => _.isFunction(o)
|
||||||
|
),
|
||||||
validate
|
validate
|
||||||
}, _.isEmpty));
|
},
|
||||||
|
_.isEmpty
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
}
|
}
|
||||||
@ -153,25 +203,44 @@ module.exports = strapi => {
|
|||||||
// /!\ Could override main router's routes.
|
// /!\ Could override main router's routes.
|
||||||
if (!_.isEmpty(excludedRoutes)) {
|
if (!_.isEmpty(excludedRoutes)) {
|
||||||
_.forEach(excludedRoutes, value => {
|
_.forEach(excludedRoutes, value => {
|
||||||
if (_.isEmpty(_.get(value, 'method')) || _.isEmpty(_.get(value, 'path'))) {
|
if (
|
||||||
|
_.isEmpty(_.get(value, 'method')) ||
|
||||||
|
_.isEmpty(_.get(value, 'path'))
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpoint = `${value.method} ${value.path}`;
|
const endpoint = `${value.method} ${value.path}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {policies, action, validate} = routerChecker(value, endpoint, plugin);
|
const { policies, action, validate } = routerChecker(
|
||||||
|
value,
|
||||||
|
endpoint,
|
||||||
|
plugin
|
||||||
|
);
|
||||||
|
|
||||||
if (_.isUndefined(action) || !_.isFunction(action)) {
|
if (_.isUndefined(action) || !_.isFunction(action)) {
|
||||||
return strapi.log.warn('Ignored attempt to bind route `' + endpoint + '` to unknown controller/action.');
|
return strapi.log.warn(
|
||||||
|
'Ignored attempt to bind route `' +
|
||||||
|
endpoint +
|
||||||
|
'` to unknown controller/action.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
strapi.router.route(_.omitBy({
|
strapi.router.route(
|
||||||
|
_.omitBy(
|
||||||
|
{
|
||||||
method: value.method,
|
method: value.method,
|
||||||
path: value.path,
|
path: value.path,
|
||||||
handler: _.remove([strapi.middlewares.compose(policies), action], o => _.isFunction(o)),
|
handler: _.remove(
|
||||||
|
[strapi.middlewares.compose(policies), action],
|
||||||
|
o => _.isFunction(o)
|
||||||
|
),
|
||||||
validate
|
validate
|
||||||
}, _.isEmpty));
|
},
|
||||||
|
_.isEmpty
|
||||||
|
)
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
}
|
}
|
||||||
@ -187,11 +256,13 @@ module.exports = strapi => {
|
|||||||
|
|
||||||
// Let the router use our routes and allowed methods.
|
// Let the router use our routes and allowed methods.
|
||||||
strapi.app.use(strapi.router.middleware());
|
strapi.app.use(strapi.router.middleware());
|
||||||
strapi.app.use(strapi.router.router.allowedMethods({
|
strapi.app.use(
|
||||||
|
strapi.router.router.allowedMethods({
|
||||||
throw: false,
|
throw: false,
|
||||||
notImplemented: () => Boom.notImplemented(),
|
notImplemented: () => Boom.notImplemented(),
|
||||||
methodNotAllowed: () => Boom.methodNotAllowed()
|
methodNotAllowed: () => Boom.methodNotAllowed()
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
|
|
||||||
strapi.app.use(responsesPolicy);
|
strapi.app.use(responsesPolicy);
|
||||||
|
|
||||||
@ -218,7 +289,10 @@ module.exports = strapi => {
|
|||||||
|
|
||||||
// Define controller and action names.
|
// Define controller and action names.
|
||||||
const handler = _.trim(value.handler).split('.');
|
const handler = _.trim(value.handler).split('.');
|
||||||
const controller = plugin ? strapi.plugins[plugin].controllers[handler[0].toLowerCase()] : strapi.controllers[handler[0].toLowerCase()] || strapi.admin.controllers[handler[0].toLowerCase()];
|
const controller = plugin
|
||||||
|
? strapi.plugins[plugin].controllers[handler[0].toLowerCase()]
|
||||||
|
: strapi.controllers[handler[0].toLowerCase()] ||
|
||||||
|
strapi.admin.controllers[handler[0].toLowerCase()];
|
||||||
const action = controller[handler[1]];
|
const action = controller[handler[1]];
|
||||||
|
|
||||||
// Retrieve the API's name where the controller is located
|
// Retrieve the API's name where the controller is located
|
||||||
@ -235,11 +309,17 @@ module.exports = strapi => {
|
|||||||
policies.push(responsesPolicy);
|
policies.push(responsesPolicy);
|
||||||
|
|
||||||
// Allow string instead of array of policies.
|
// Allow string instead of array of policies.
|
||||||
if (!_.isArray(_.get(value, 'config.policies')) && !_.isEmpty(_.get(value, 'config.policies'))) {
|
if (
|
||||||
|
!_.isArray(_.get(value, 'config.policies')) &&
|
||||||
|
!_.isEmpty(_.get(value, 'config.policies'))
|
||||||
|
) {
|
||||||
value.config.policies = [value.config.policies];
|
value.config.policies = [value.config.policies];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_.isArray(_.get(value, 'config.policies')) && !_.isEmpty(_.get(value, 'config.policies'))) {
|
if (
|
||||||
|
_.isArray(_.get(value, 'config.policies')) &&
|
||||||
|
!_.isEmpty(_.get(value, 'config.policies'))
|
||||||
|
) {
|
||||||
_.forEach(value.config.policies, policy => {
|
_.forEach(value.config.policies, policy => {
|
||||||
// Define global policy prefix.
|
// Define global policy prefix.
|
||||||
const globalPolicyPrefix = 'global.';
|
const globalPolicyPrefix = 'global.';
|
||||||
@ -247,33 +327,103 @@ module.exports = strapi => {
|
|||||||
const policySplited = policy.split('.');
|
const policySplited = policy.split('.');
|
||||||
|
|
||||||
// Looking for global policy or namespaced.
|
// Looking for global policy or namespaced.
|
||||||
if (_.startsWith(policy, globalPolicyPrefix, 0) && !_.isEmpty(strapi.config.policies, policy.replace(globalPolicyPrefix, ''))) {
|
if (
|
||||||
|
_.startsWith(policy, globalPolicyPrefix, 0) &&
|
||||||
|
!_.isEmpty(
|
||||||
|
strapi.config.policies,
|
||||||
|
policy.replace(globalPolicyPrefix, '')
|
||||||
|
)
|
||||||
|
) {
|
||||||
// Global policy.
|
// Global policy.
|
||||||
return policies.push(strapi.config.policies[policy.replace(globalPolicyPrefix, '').toLowerCase()]);
|
return policies.push(
|
||||||
} else if (_.startsWith(policy, pluginPolicyPrefix, 0) && strapi.plugins[policySplited[1]] && !_.isEmpty(_.get(strapi.plugins, policySplited[1] + '.config.policies.' + policySplited[2].toLowerCase()))) {
|
strapi.config.policies[
|
||||||
|
policy.replace(globalPolicyPrefix, '').toLowerCase()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
_.startsWith(policy, pluginPolicyPrefix, 0) &&
|
||||||
|
strapi.plugins[policySplited[1]] &&
|
||||||
|
!_.isEmpty(
|
||||||
|
_.get(
|
||||||
|
strapi.plugins,
|
||||||
|
policySplited[1] +
|
||||||
|
'.config.policies.' +
|
||||||
|
policySplited[2].toLowerCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
// Plugin's policies can be used from app APIs with a specific syntax (`plugins.pluginName.policyName`).
|
// Plugin's policies can be used from app APIs with a specific syntax (`plugins.pluginName.policyName`).
|
||||||
return policies.push(_.get(strapi.plugins, policySplited[1] + '.config.policies.' + policySplited[2].toLowerCase()));
|
return policies.push(
|
||||||
} else if (!_.startsWith(policy, globalPolicyPrefix, 0) && plugin && !_.isEmpty(_.get(strapi.plugins, plugin + '.config.policies.' + policy.toLowerCase()))) {
|
_.get(
|
||||||
|
strapi.plugins,
|
||||||
|
policySplited[1] +
|
||||||
|
'.config.policies.' +
|
||||||
|
policySplited[2].toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
!_.startsWith(policy, globalPolicyPrefix, 0) &&
|
||||||
|
plugin &&
|
||||||
|
!_.isEmpty(
|
||||||
|
_.get(
|
||||||
|
strapi.plugins,
|
||||||
|
plugin + '.config.policies.' + policy.toLowerCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
// Plugin policy used in the plugin itself.
|
// Plugin policy used in the plugin itself.
|
||||||
return policies.push(_.get(strapi.plugins, plugin + '.config.policies.' + policy.toLowerCase()));
|
return policies.push(
|
||||||
} else if (!_.startsWith(policy, globalPolicyPrefix, 0) && !_.isEmpty(_.get(strapi.api, currentApiName + '.config.policies.' + policy.toLowerCase()))) {
|
_.get(
|
||||||
|
strapi.plugins,
|
||||||
|
plugin + '.config.policies.' + policy.toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
!_.startsWith(policy, globalPolicyPrefix, 0) &&
|
||||||
|
!_.isEmpty(
|
||||||
|
_.get(
|
||||||
|
strapi.api,
|
||||||
|
currentApiName + '.config.policies.' + policy.toLowerCase()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
// API policy used in the API itself.
|
// API policy used in the API itself.
|
||||||
return policies.push(_.get(strapi.api, currentApiName + '.config.policies.' + policy.toLowerCase()));
|
return policies.push(
|
||||||
|
_.get(
|
||||||
|
strapi.api,
|
||||||
|
currentApiName + '.config.policies.' + policy.toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
strapi.log.error('Ignored attempt to bind route `' + endpoint + '` with unknown policy `' + policy + '`.');
|
strapi.log.error(
|
||||||
|
'Ignored attempt to bind route `' +
|
||||||
|
endpoint +
|
||||||
|
'` with unknown policy `' +
|
||||||
|
policy +
|
||||||
|
'`.'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init validate.
|
// Init validate.
|
||||||
const validate = {};
|
const validate = {};
|
||||||
|
|
||||||
if (_.isString(_.get(value, 'config.validate')) && !_.isEmpty(_.get(value, 'config.validate'))) {
|
if (
|
||||||
const validator = _.get(strapi.api, currentApiName + '.validators.' + value.config.validate);
|
_.isString(_.get(value, 'config.validate')) &&
|
||||||
|
!_.isEmpty(_.get(value, 'config.validate'))
|
||||||
|
) {
|
||||||
|
const validator = _.get(
|
||||||
|
strapi.api,
|
||||||
|
currentApiName + '.validators.' + value.config.validate
|
||||||
|
);
|
||||||
|
|
||||||
_.merge(validate, _.mapValues(validator, value => {
|
_.merge(
|
||||||
|
validate,
|
||||||
|
_.mapValues(validator, value => {
|
||||||
return builder.build(value);
|
return builder.build(value);
|
||||||
}));
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -34,13 +34,14 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^2.1.2",
|
"async": "^2.1.2",
|
||||||
|
"await-busboy": "^1.0.1",
|
||||||
"boom": "^4.2.0",
|
"boom": "^4.2.0",
|
||||||
"consolidate": "~0.14.0",
|
"consolidate": "~0.14.0",
|
||||||
"delegates": "^1.0.0",
|
"delegates": "^1.0.0",
|
||||||
"kcors": "^2.2.0",
|
"kcors": "^2.2.0",
|
||||||
"koa": "^2.1.0",
|
"koa": "^2.1.0",
|
||||||
"koa-better-static": "^1.0.5",
|
"koa-better-static": "^1.0.5",
|
||||||
"koa-bodyparser": "^4.1.0",
|
"koa-body": "^2.0.0",
|
||||||
"koa-compose": "^3.2.1",
|
"koa-compose": "^3.2.1",
|
||||||
"koa-compress": "^2.0.0",
|
"koa-compress": "^2.0.0",
|
||||||
"koa-convert": "^1.2.0",
|
"koa-convert": "^1.2.0",
|
||||||
|
|||||||
@ -16,7 +16,7 @@ describe('middlewares', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('`strapi.middlewares.bodyparser` should be a function', () => {
|
it('`strapi.middlewares.bodyparser` should be a function', () => {
|
||||||
assert(typeof strapi.middlewares.bodyparser === 'function');
|
assert(typeof strapi.middlewares.body === 'function');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('`strapi.middlewares.compose` should be a function', () => {
|
it('`strapi.middlewares.compose` should be a function', () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user