From 6abea9cd85abf47e1b582f1bbc0a667e5cea6b0c Mon Sep 17 00:00:00 2001 From: Convly Date: Tue, 23 Aug 2022 16:02:45 +0200 Subject: [PATCH] Expose "get actions as map" & update sync actions Co-authored-by: Ben Irvin --- packages/core/strapi/lib/Strapi.js | 2 +- .../strapi/lib/services/content-api/index.js | 93 +++++++++++++------ .../permissions/providers/action.js | 4 +- .../server/services/users-permissions.js | 2 - 4 files changed, 70 insertions(+), 31 deletions(-) diff --git a/packages/core/strapi/lib/Strapi.js b/packages/core/strapi/lib/Strapi.js index 30c77ef821..f7c9c37357 100644 --- a/packages/core/strapi/lib/Strapi.js +++ b/packages/core/strapi/lib/Strapi.js @@ -454,7 +454,7 @@ class Strapi { await this.runLifecyclesFunctions(LIFECYCLES.BOOTSTRAP); // TODO: is this the best place for this? - await this.contentAPI.permissions.syncActions(); + await this.contentAPI.permissions.registerActions(); this.cron.start(); diff --git a/packages/core/strapi/lib/services/content-api/index.js b/packages/core/strapi/lib/services/content-api/index.js index db5be6bf0a..c17922bc25 100644 --- a/packages/core/strapi/lib/services/content-api/index.js +++ b/packages/core/strapi/lib/services/content-api/index.js @@ -1,6 +1,6 @@ 'use strict'; -const { uniq } = require('lodash'); +const _ = require('lodash'); const permissions = require('./permissions'); /** @@ -30,30 +30,70 @@ const createContentAPI = (/* strapi */) => { condition: permissions.providers.createConditionProvider(), }; - const syncActions = async () => { - // Register actions - const apiRoutesName = Object.values(strapi.api) - .map((api) => api.routes) - .reduce((acc, routesMap) => { - const routes = Object.values(routesMap) - // Only content api routes - .filter((p) => p.type === 'content-api') - // Resolve every handler name for each route - .reduce((a, p) => a.concat(p.routes.map((i) => i.handler)), []); - return acc.concat(routes); - }, []); - const pluginsRoutesname = Object.values(strapi.plugins) - .map((plugin) => plugin.routes['content-api'] || {}) - .map((p) => (p.routes || []).map((i) => i.handler)) - .flat(); - const actions = apiRoutesName.concat(pluginsRoutesname); - Promise.all( - uniq(actions).map((action) => - providers.action.register(action).catch(() => { - strapi.log.warn(`Trying to add action that already exists: ${action}`); - }) - ) - ); + 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 }) + .catch(() => { + strapi.log.warn(`Trying to add action that already exists: ${actionUID}`); + }); + }) + ); + } + } }; // create permission engine @@ -65,7 +105,8 @@ const createContentAPI = (/* strapi */) => { permissions: { engine, providers, - syncActions, + registerActions, + getActionsMap, }, }; }; diff --git a/packages/core/strapi/lib/services/content-api/permissions/providers/action.js b/packages/core/strapi/lib/services/content-api/permissions/providers/action.js index e44cce17a2..ead6855351 100644 --- a/packages/core/strapi/lib/services/content-api/permissions/providers/action.js +++ b/packages/core/strapi/lib/services/content-api/permissions/providers/action.js @@ -8,12 +8,12 @@ module.exports = (options = {}) => { return { ...provider, - async register(action) { + async register(action, payload) { if (strapi.isLoaded) { throw new Error(`You can't register new actions outside the bootstrap function.`); } - return provider.register(action, { name: action }); + return provider.register(action, payload); }, }; }; diff --git a/packages/plugins/users-permissions/server/services/users-permissions.js b/packages/plugins/users-permissions/server/services/users-permissions.js index 41f256334b..1389cd582e 100644 --- a/packages/plugins/users-permissions/server/services/users-permissions.js +++ b/packages/plugins/users-permissions/server/services/users-permissions.js @@ -171,8 +171,6 @@ module.exports = ({ strapi }) => ({ const toDelete = _.difference(permissionsFoundInDB, allActions); - // NOTE: actions are registered in content API bootstrap syncActions - await Promise.all( toDelete.map((action) => { return strapi.query('plugin::users-permissions.permission').delete({ where: { action } });