Merge pull request #14271 from strapi/api-token-v2/cleanup-content-api-core-service

Core Content API service cleanup
This commit is contained in:
Jean-Sébastien Herbaux 2022-08-31 16:33:02 +02:00 committed by GitHub
commit 344d200582
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 110 deletions

View File

@ -75,7 +75,7 @@ class Strapi {
// Load the app configuration from the dist directory
const appConfig = loadConfiguration({ appDir: rootDirs.app, distDir: rootDirs.dist }, opts);
// Instanciate the Strapi container
// Instantiate the Strapi container
this.container = createContainer(this);
// Register every Strapi registry in the container
@ -100,7 +100,7 @@ class Strapi {
this.isLoaded = false;
this.reload = this.reload();
// Instanciate the Koa app & the HTTP server
// Instantiate the Koa app & the HTTP server
this.server = createServer(this);
// Strapi utils instanciation

View File

@ -1,114 +1,13 @@
'use strict';
const _ = require('lodash');
const permissions = require('./permissions');
/**
* Creates an handler which check that the permission's action exists in the action registry
*/
const createValidatePermissionHandler =
(actionProvider) =>
({ permission }) => {
const action = actionProvider.get(permission.action);
// If the action isn't registered into the action provider, then ignore the permission and warn the user
if (!action) {
strapi.log.debug(
`Unknown action "${permission.action}" supplied when registering a new permission`
);
return false;
}
};
const instantiatePermissionsUtilities = require('./permissions');
/**
* Create a content API container that holds logic, tools and utils. (eg: permissions, ...)
*/
const createContentAPI = (strapi) => {
// Add providers
const providers = {
action: permissions.providers.createActionProvider(),
condition: permissions.providers.createConditionProvider(),
};
const getActionsMap = () => {
const actionMap = {};
const isContentApi = (action) => {
if (!_.has(action, Symbol.for('__type__'))) {
return false;
}
return action[Symbol.for('__type__')].includes('content-api');
};
const registerAPIsActions = (apis, source) => {
_.forEach(apis, (api, apiName) => {
const controllers = _.reduce(
api.controllers,
(acc, controller, controllerName) => {
const contentApiActions = _.pickBy(controller, isContentApi);
if (_.isEmpty(contentApiActions)) {
return acc;
}
acc[controllerName] = Object.keys(contentApiActions);
return acc;
},
{}
);
if (!_.isEmpty(controllers)) {
actionMap[`${source}::${apiName}`] = { controllers };
}
});
};
registerAPIsActions(strapi.api, 'api');
registerAPIsActions(strapi.plugins, 'plugin');
return actionMap;
};
const registerActions = async () => {
const actionsMap = getActionsMap();
// For each API
for (const [api, value] of Object.entries(actionsMap)) {
const { controllers } = value;
// Register controllers methods as actions
for (const [controller, actions] of Object.entries(controllers)) {
// Register each action individually
await Promise.all(
actions.map((action) => {
const actionUID = `${api}.${controller}.${action}`;
return providers.action.register(actionUID, {
api,
controller,
action,
uid: actionUID,
});
})
);
}
}
};
// create permission engine
const engine = permissions
.createPermissionEngine({ providers })
.on('before-format::validate.permission', createValidatePermissionHandler(providers.action));
return {
permissions: {
engine,
providers,
registerActions,
getActionsMap,
},
permissions: instantiatePermissionsUtilities(strapi),
};
};

View File

@ -1,9 +1,148 @@
'use strict';
const providers = require('./providers');
const _ = require('lodash');
const { createActionProvider, createConditionProvider } = require('./providers');
const createPermissionEngine = require('./engine');
module.exports = {
providers,
createPermissionEngine,
/**
* Creates an handler that checks if the permission's action exists in the action registry
*/
const createValidatePermissionHandler =
(actionProvider) =>
({ permission }) => {
const action = actionProvider.get(permission.action);
// If the action isn't registered into the action provider, then ignore the permission and warn the user
if (!action) {
strapi.log.debug(
`Unknown action "${permission.action}" supplied when registering a new permission`
);
return false;
}
};
/**
* Create instances of providers and permission engine for the core content-API service.
* Also, expose utilities to get informations about available actions and such.
*
* @param {Strapi.Strapi} strapi
*/
module.exports = (strapi) => {
// NOTE: Here we define both an action and condition provider,
// but at the moment, we're only using the action one.
const providers = {
action: createActionProvider(),
condition: createConditionProvider(),
};
/**
* Get a tree representation of the available Content API actions
* based on the methods of the Content API controllers.
*
* @note Only actions bound to a content-API route are returned.
*
* @return {{ [api: string]: { [controller: string]: string[] }}}
*/
const getActionsMap = () => {
const actionMap = {};
/**
* Check if a controller's action is bound to the
* content-api by looking at a potential __type__ symbol
*
* @param {object} action
*
* @return {boolean}
*/
const isContentApi = (action) => {
if (!_.has(action, Symbol.for('__type__'))) {
return false;
}
return action[Symbol.for('__type__')].includes('content-api');
};
/**
* Register actions from a specific API source into the result tree
*
* @param {{ [apiName]: { controllers: { [controller]: object } }}} apis The API container
* @param {string} source The prefix to use in front the API name
*
* @return {void}
*/
const registerAPIsActions = (apis, source) => {
_.forEach(apis, (api, apiName) => {
const controllers = _.reduce(
api.controllers,
(acc, controller, controllerName) => {
const contentApiActions = _.pickBy(controller, isContentApi);
if (_.isEmpty(contentApiActions)) {
return acc;
}
acc[controllerName] = Object.keys(contentApiActions);
return acc;
},
{}
);
if (!_.isEmpty(controllers)) {
actionMap[`${source}::${apiName}`] = { controllers };
}
});
};
registerAPIsActions(strapi.api, 'api');
registerAPIsActions(strapi.plugins, 'plugin');
return actionMap;
};
/**
* Register all the content-API's controllers actions into the action provider.
* This method make use of the {@link getActionsMap} to generate the list of actions to register.
*
* @return {void}
*/
const registerActions = async () => {
const actionsMap = getActionsMap();
// For each API
for (const [api, value] of Object.entries(actionsMap)) {
const { controllers } = value;
// Register controllers methods as actions
for (const [controller, actions] of Object.entries(controllers)) {
// Register each action individually
await Promise.all(
actions.map((action) => {
const actionUID = `${api}.${controller}.${action}`;
return providers.action.register(actionUID, {
api,
controller,
action,
uid: actionUID,
});
})
);
}
}
};
// Create an instance of a content-API permission engine
// and binds a custom validation handler to it
const engine = createPermissionEngine({ providers }).on(
'before-format::validate.permission',
createValidatePermissionHandler(providers.action)
);
return {
engine,
providers,
registerActions,
getActionsMap,
};
};

View File

@ -52,7 +52,7 @@ export const useCropImg = () => {
if (!cropperRef.current) {
reject(
new Error(
'The cropper has not been instanciated: make sure to call the crop() function before calling produceFile().'
'The cropper has not been instantiated: make sure to call the crop() function before calling produceFile().'
)
);
} else {