From 13265dac9461c5db4ea983ac69d5c46bf4a45bf1 Mon Sep 17 00:00:00 2001 From: Marc-Roig Date: Tue, 23 May 2023 17:22:35 +0200 Subject: [PATCH 01/23] feat: expose allowed webhook events in webhook store --- packages/core/strapi/lib/services/webhook-store.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index 185dbaeb43..b4efb7c8e5 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -50,7 +50,20 @@ const fromDBObject = (row) => { const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); + // TODO: Use a Map + const webhookEvents = new Map([ + ['ENTRY_CREATE', 'entry.create'], + ['ENTRY_UPDATE', 'entry.update'], + ['ENTRY_DELETE', 'entry.delete'], + ['ENTRY_PUBLISH', 'entry.publish'], + ['ENTRY_UNPUBLISH', 'entry.unpublish'], + ['MEDIA_CREATE', 'media.create'], + ['MEDIA_UPDATE', 'media.update'], + ['MEDIA_DELETE', 'media.delete'], + ]); + return { + webhookEvents, async findWebhooks() { const results = await webhookQueries.findMany(); From 994a414fdcf955d75545b3bc8622748c934b3a92 Mon Sep 17 00:00:00 2001 From: Marc-Roig Date: Tue, 23 May 2023 17:23:42 +0200 Subject: [PATCH 02/23] chore: rename allowedEvents --- packages/core/strapi/lib/services/webhook-store.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index b4efb7c8e5..163c616690 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -51,7 +51,7 @@ const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); // TODO: Use a Map - const webhookEvents = new Map([ + const allowedEvents = new Map([ ['ENTRY_CREATE', 'entry.create'], ['ENTRY_UPDATE', 'entry.update'], ['ENTRY_DELETE', 'entry.delete'], @@ -63,7 +63,7 @@ const createWebhookStore = ({ db }) => { ]); return { - webhookEvents, + allowedEvents, async findWebhooks() { const results = await webhookQueries.findMany(); @@ -76,6 +76,7 @@ const createWebhookStore = ({ db }) => { }, createWebhook(data) { + // TODO: Validate webhook event return webhookQueries .create({ data: toDBObject({ ...data, isEnabled: true }), @@ -84,6 +85,7 @@ const createWebhookStore = ({ db }) => { }, async updateWebhook(id, data) { + // TODO: Validate webhook event const webhook = await webhookQueries.update({ where: { id }, data: toDBObject(data), From 933a2e3dff1b3b6d296388cade44b3974da03902 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Wed, 24 May 2023 16:46:59 +0100 Subject: [PATCH 03/23] feature(upload): register webhook events --- packages/core/strapi/lib/services/webhook-store.js | 4 ---- packages/core/upload/server/bootstrap.js | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index 163c616690..b5d4e31ae6 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -50,16 +50,12 @@ const fromDBObject = (row) => { const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); - // TODO: Use a Map const allowedEvents = new Map([ ['ENTRY_CREATE', 'entry.create'], ['ENTRY_UPDATE', 'entry.update'], ['ENTRY_DELETE', 'entry.delete'], ['ENTRY_PUBLISH', 'entry.publish'], ['ENTRY_UNPUBLISH', 'entry.unpublish'], - ['MEDIA_CREATE', 'media.create'], - ['MEDIA_UPDATE', 'media.update'], - ['MEDIA_DELETE', 'media.delete'], ]); return { diff --git a/packages/core/upload/server/bootstrap.js b/packages/core/upload/server/bootstrap.js index c45e6d5df6..cc0f7354f3 100644 --- a/packages/core/upload/server/bootstrap.js +++ b/packages/core/upload/server/bootstrap.js @@ -36,11 +36,18 @@ module.exports = async ({ strapi }) => { } await registerPermissionActions(); + await registerWebhookEvents(); await getService('weeklyMetrics').registerCron(); getService('metrics').sendUploadPluginMetrics(); }; +const registerWebhookEvents = async () => + strapi.webhookStore.allowedEvents + .set('MEDIA_CREATE', 'media.create') + .set('MEDIA_UPDATE', 'media.update') + .set('MEDIA_DELETE', 'media.delete'); + const registerPermissionActions = async () => { const actions = [ { From 1c66cdf679822c12b935a31a05e640bbac733a54 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Wed, 24 May 2023 16:56:05 +0100 Subject: [PATCH 04/23] feature(ee): register review workflow webhook events --- .../ee/server/services/review-workflows/review-workflows.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js index 77bf2d580d..2207f54617 100644 --- a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js +++ b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js @@ -108,6 +108,9 @@ function persistStagesJoinTables({ strapi }) { }; } +const registerWebhookEvents = async () => + strapi.webhookStore.allowedEvents.set('WORKFLOW_STAGE_CHANGE', 'workflow.stage.change'); + module.exports = ({ strapi }) => { const workflowsService = getService('workflows', { strapi }); const stagesService = getService('stages', { strapi }); @@ -115,6 +118,7 @@ module.exports = ({ strapi }) => { return { async bootstrap() { await initDefaultWorkflow({ workflowsService, stagesService, strapi }); + await registerWebhookEvents(); }, async register() { extendReviewWorkflowContentTypes({ strapi }); From 506c495d91584da43e292b13b1acfa1c0898a1fe Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 12:34:52 +0100 Subject: [PATCH 05/23] feature: replace webhook validation with webhook store --- .../core/admin/server/controllers/webhooks.js | 6 ++++-- packages/core/utils/lib/index.js | 2 -- packages/core/utils/lib/webhook.js | 16 ---------------- 3 files changed, 4 insertions(+), 20 deletions(-) delete mode 100644 packages/core/utils/lib/webhook.js diff --git a/packages/core/admin/server/controllers/webhooks.js b/packages/core/admin/server/controllers/webhooks.js index 9547f08377..f4dbd5921d 100644 --- a/packages/core/admin/server/controllers/webhooks.js +++ b/packages/core/admin/server/controllers/webhooks.js @@ -1,7 +1,7 @@ 'use strict'; const _ = require('lodash'); -const { yup, webhook: webhookUtils, validateYupSchema } = require('@strapi/utils'); +const { yup, validateYupSchema } = require('@strapi/utils'); const urlRegex = /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)(?:\.(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/; @@ -23,7 +23,9 @@ const webhookValidator = yup ) .required(); }), - events: yup.array().of(yup.string().oneOf(_.values(webhookUtils.webhookEvents)).required()), + events: yup + .array() + .of(yup.string().oneOf(Array.from(strapi.webhookStore.allowedEvents.values())).required()), }) .noUnknown(); diff --git a/packages/core/utils/lib/index.js b/packages/core/utils/lib/index.js index d9cdead14f..937944b811 100644 --- a/packages/core/utils/lib/index.js +++ b/packages/core/utils/lib/index.js @@ -28,7 +28,6 @@ const { removeUndefined, keysDeep } = require('./object-formatting'); const { getConfigUrls, getAbsoluteAdminUrl, getAbsoluteServerUrl } = require('./config'); const { generateTimestampCode } = require('./code-generator'); const contentTypes = require('./content-types'); -const webhook = require('./webhook'); const env = require('./env-helper'); const relations = require('./relations'); const setCreatorFields = require('./set-creator-fields'); @@ -75,7 +74,6 @@ module.exports = { isCamelCase, toKebabCase, contentTypes, - webhook, env, relations, setCreatorFields, diff --git a/packages/core/utils/lib/webhook.js b/packages/core/utils/lib/webhook.js deleted file mode 100644 index 4bade8ecf9..0000000000 --- a/packages/core/utils/lib/webhook.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const webhookEvents = { - ENTRY_CREATE: 'entry.create', - ENTRY_UPDATE: 'entry.update', - ENTRY_DELETE: 'entry.delete', - ENTRY_PUBLISH: 'entry.publish', - ENTRY_UNPUBLISH: 'entry.unpublish', - MEDIA_CREATE: 'media.create', - MEDIA_UPDATE: 'media.update', - MEDIA_DELETE: 'media.delete', -}; - -module.exports = { - webhookEvents, -}; From 14ff035647e0668e8d6ec0e9505b94dc77a1003e Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 12:37:38 +0100 Subject: [PATCH 06/23] feature: replace emitEvent calls with new webhook store --- .../server/services/entity-manager.js | 5 ++-- .../lib/services/entity-service/index.js | 25 ++++++++++--------- .../core/upload/server/services/upload.js | 9 +++---- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/core/content-manager/server/services/entity-manager.js b/packages/core/content-manager/server/services/entity-manager.js index 28393bddad..612ab2aa4e 100644 --- a/packages/core/content-manager/server/services/entity-manager.js +++ b/packages/core/content-manager/server/services/entity-manager.js @@ -10,7 +10,6 @@ const { sumDraftCounts } = require('./utils/draft'); const { hasDraftAndPublish } = strapiUtils.contentTypes; const { PUBLISHED_AT_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = strapiUtils.contentTypes.constants; -const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = strapiUtils.webhook.webhookEvents; const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE); @@ -254,7 +253,7 @@ module.exports = ({ strapi }) => ({ const updatedEntity = await strapi.entityService.update(uid, entity.id, params); - await emitEvent(ENTRY_PUBLISH, updatedEntity, uid); + await emitEvent(strapi.webhookStore.allowedEvents.get('ENTRY_PUBLISH'), updatedEntity, uid); const mappedEntity = await this.mapEntity(updatedEntity, uid); @@ -283,7 +282,7 @@ module.exports = ({ strapi }) => ({ const updatedEntity = await strapi.entityService.update(uid, entity.id, params); - await emitEvent(ENTRY_UNPUBLISH, updatedEntity, uid); + await emitEvent(strapi.webhookStore.allowedEvents.get('ENTRY_UNPUBLISH'), updatedEntity, uid); const mappedEntity = await this.mapEntity(updatedEntity, uid); diff --git a/packages/core/strapi/lib/services/entity-service/index.js b/packages/core/strapi/lib/services/entity-service/index.js index 2cf70b83de..870ac7f9e2 100644 --- a/packages/core/strapi/lib/services/entity-service/index.js +++ b/packages/core/strapi/lib/services/entity-service/index.js @@ -4,11 +4,7 @@ const _ = require('lodash'); const delegate = require('delegates'); const { InvalidTimeError, InvalidDateError, InvalidDateTimeError, InvalidRelationError } = require('@strapi/database').errors; -const { - webhook: webhookUtils, - contentTypes: contentTypesUtils, - sanitize, -} = require('@strapi/utils'); +const { contentTypes: contentTypesUtils, sanitize } = require('@strapi/utils'); const { ValidationError } = require('@strapi/utils').errors; const { isAnyToMany } = require('@strapi/utils').relations; const { transformParamsToQuery } = require('@strapi/utils').convertQueryParams; @@ -31,9 +27,6 @@ const transformLoadParamsToQuery = (uid, field, params = {}, pagination = {}) => }; }; -// TODO: those should be strapi events used by the webhooks not the other way arround -const { ENTRY_CREATE, ENTRY_UPDATE, ENTRY_DELETE } = webhookUtils.webhookEvents; - const databaseErrorsToTransform = [ InvalidTimeError, InvalidDateTimeError, @@ -162,7 +155,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entity = await this.findOne(uid, entity.id, wrappedParams); } - await this.emitEvent(uid, ENTRY_CREATE, entity); + await this.emitEvent(uid, strapi.webhookStore.allowedEvents.get('ENTRY_CREATE'), entity); return entity; }, @@ -213,7 +206,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entity = await this.findOne(uid, entity.id, wrappedParams); } - await this.emitEvent(uid, ENTRY_UPDATE, entity); + await this.emitEvent(uid, strapi.webhookStore.allowedEvents.get('ENTRY_UPDATE'), entity); return entity; }, @@ -238,7 +231,11 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) await db.query(uid).delete({ where: { id: entityToDelete.id } }); await deleteComponents(uid, componentsToDelete, { loadComponents: false }); - await this.emitEvent(uid, ENTRY_DELETE, entityToDelete); + await this.emitEvent( + uid, + strapi.webhookStore.allowedEvents.get('ENTRY_DELETE'), + entityToDelete + ); return entityToDelete; }, @@ -266,7 +263,11 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) ); // Trigger webhooks. One for each entity - await Promise.all(entitiesToDelete.map((entity) => this.emitEvent(uid, ENTRY_DELETE, entity))); + await Promise.all( + entitiesToDelete.map((entity) => + this.emitEvent(uid, strapi.webhookStore.allowedEvents.get('ENTRY_DELETE'), entity) + ) + ); return deletedEntities; }, diff --git a/packages/core/upload/server/services/upload.js b/packages/core/upload/server/services/upload.js index 45fcc8b360..1297a5646f 100644 --- a/packages/core/upload/server/services/upload.js +++ b/packages/core/upload/server/services/upload.js @@ -17,13 +17,10 @@ const { sanitize, nameToSlug, contentTypes: contentTypesUtils, - webhook: webhookUtils, errors: { ApplicationError, NotFoundError }, file: { bytesToKbytes }, } = require('@strapi/utils'); -const { MEDIA_UPDATE, MEDIA_CREATE, MEDIA_DELETE } = webhookUtils.webhookEvents; - const { FILE_MODEL_UID } = require('../constants'); const { getService } = require('../utils'); @@ -347,7 +344,7 @@ module.exports = ({ strapi }) => ({ const res = await strapi.entityService.update(FILE_MODEL_UID, id, { data: fileValues }); - await this.emitEvent(MEDIA_UPDATE, res); + await this.emitEvent(strapi.webhookStore.allowedEvents.get('MEDIA_UPDATE'), res); return res; }, @@ -363,7 +360,7 @@ module.exports = ({ strapi }) => ({ const res = await strapi.query(FILE_MODEL_UID).create({ data: fileValues }); - await this.emitEvent(MEDIA_CREATE, res); + await this.emitEvent(strapi.webhookStore.allowedEvents.get('MEDIA_CREATE'), res); return res; }, @@ -400,7 +397,7 @@ module.exports = ({ strapi }) => ({ where: { id: file.id }, }); - await this.emitEvent(MEDIA_DELETE, media); + await this.emitEvent(strapi.webhookStore.allowedEvents.get('MEDIA_DELETE'), media); return strapi.query(FILE_MODEL_UID).delete({ where: { id: file.id } }); }, From aac731d7a068c4ca3f6873d9e3796ad7cae535fd Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 13:07:06 +0100 Subject: [PATCH 07/23] fix: move webhook form validation to webhook-store --- .../core/admin/server/controllers/webhooks.js | 38 +---------------- .../core/strapi/lib/services/webhook-store.js | 42 +++++++++++++++++-- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/packages/core/admin/server/controllers/webhooks.js b/packages/core/admin/server/controllers/webhooks.js index f4dbd5921d..fd97f2437b 100644 --- a/packages/core/admin/server/controllers/webhooks.js +++ b/packages/core/admin/server/controllers/webhooks.js @@ -1,38 +1,5 @@ 'use strict'; -const _ = require('lodash'); -const { yup, validateYupSchema } = require('@strapi/utils'); - -const urlRegex = - /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)(?:\.(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/; - -const webhookValidator = yup - .object({ - name: yup.string().required(), - url: yup.string().matches(urlRegex, 'url must be a valid URL').required(), - headers: yup.lazy((data) => { - if (typeof data !== 'object') { - return yup.object().required(); - } - - return yup - .object( - _.mapValues(data, () => { - yup.string().min(1).required(); - }) - ) - .required(); - }), - events: yup - .array() - .of(yup.string().oneOf(Array.from(strapi.webhookStore.allowedEvents.values())).required()), - }) - .noUnknown(); - -const updateWebhookValidator = webhookValidator.shape({ - isEnabled: yup.boolean(), -}); - module.exports = { async listWebhooks(ctx) { const webhooks = await strapi.webhookStore.findWebhooks(); @@ -53,8 +20,6 @@ module.exports = { async createWebhook(ctx) { const { body } = ctx.request; - await validateYupSchema(webhookValidator)(body); - const webhook = await strapi.webhookStore.createWebhook(body); strapi.webhookRunner.add(webhook); @@ -66,8 +31,6 @@ module.exports = { const { id } = ctx.params; const { body } = ctx.request; - await validateYupSchema(updateWebhookValidator)(body); - const webhook = await strapi.webhookStore.findWebhook(id); if (!webhook) { @@ -113,6 +76,7 @@ module.exports = { for (const id of ids) { const webhook = await strapi.webhookStore.findWebhook(id); + // eslint-disable-next-line no-continue if (!webhook) continue; await strapi.webhookStore.deleteWebhook(id); diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index b5d4e31ae6..90b9bf0bc5 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -4,6 +4,9 @@ 'use strict'; +const { mapValues } = require('lodash/fp'); +const { yup, validateYupSchema } = require('@strapi/utils'); + const webhookModel = { uid: 'webhook', collectionName: 'strapi_webhooks', @@ -47,6 +50,37 @@ const fromDBObject = (row) => { }; }; +const urlRegex = + /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)(?:\.(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/; + +const webhookValidator = (allowedEvents) => + yup + .object({ + name: yup.string().required(), + url: yup.string().matches(urlRegex, 'url must be a valid URL').required(), + headers: yup.lazy((data) => { + if (typeof data !== 'object') { + return yup.object().required(); + } + + return yup + .object( + mapValues(() => { + yup.string().min(1).required(); + }, data) + ) + .required(); + }), + events: yup.array().of(yup.string().oneOf(Array.from(allowedEvents.values())).required()), + }) + .noUnknown(); + +const updateWebhookValidator = (allowedEvents) => + webhookValidator(allowedEvents).shape({ + id: yup.number().required(), + isEnabled: yup.boolean().required(), + }); + const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); @@ -71,8 +105,9 @@ const createWebhookStore = ({ db }) => { return result ? fromDBObject(result) : null; }, - createWebhook(data) { - // TODO: Validate webhook event + async createWebhook(data) { + await validateYupSchema(webhookValidator(allowedEvents))(data); + return webhookQueries .create({ data: toDBObject({ ...data, isEnabled: true }), @@ -81,7 +116,8 @@ const createWebhookStore = ({ db }) => { }, async updateWebhook(id, data) { - // TODO: Validate webhook event + await validateYupSchema(updateWebhookValidator(allowedEvents))(data); + const webhook = await webhookQueries.update({ where: { id }, data: toDBObject(data), From 61a474c0b4fe03451565b6f99da4dbb3314153c7 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 13:14:39 +0100 Subject: [PATCH 08/23] test(entity-service) --- .../entity-service/__tests__/entity-service.test.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js index 089d8007b2..4167493f67 100644 --- a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js +++ b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js @@ -20,6 +20,12 @@ describe('Entity service', () => { query: jest.fn(() => ({})), }; + beforeEach(() => { + global.strapi.webhookStore = { + allowedEvents: new Map([['ENTRY_CREATE', 'entry.create']]), + }; + }); + describe('Decorator', () => { test.each(['create', 'update', 'findMany', 'findOne', 'delete', 'count', 'findPage'])( 'Can decorate', @@ -98,12 +104,15 @@ describe('Entity service', () => { global.strapi.getModel.mockImplementation((modelName) => fakeModels[modelName]); global.strapi.query.mockImplementation(() => fakeQuery); }); + beforeEach(() => { jest.clearAllMocks(); }); + afterAll(() => { global.strapi.getModel.mockImplementation(() => ({})); }); + describe('assign default values', () => { let instance; const entityUID = 'api::entity.entity'; From b249c115d4fab5542ea626f2a73b68f47aea51fb Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 13:21:14 +0100 Subject: [PATCH 09/23] test(entity-service): events --- .../__tests__/entity-service-events.test.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js index a82221cbe2..d27aeac830 100644 --- a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js +++ b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js @@ -21,6 +21,15 @@ describe('Entity service triggers webhooks', () => { instance = createEntityService({ strapi: { getModel: () => model, + webhookStore: { + allowedEvents: new Map([ + ['ENTRY_CREATE', 'entry.create'], + ['ENTRY_UPDATE', 'entry.update'], + ['ENTRY_DELETE', 'entry.delete'], + ['ENTRY_PUBLISH', 'entry.publish'], + ['ENTRY_UNPUBLISH', 'entry.unpublish'], + ]), + }, }, db: { transaction: (cb) => cb(), @@ -40,9 +49,6 @@ describe('Entity service triggers webhooks', () => { global.strapi = { getModel: () => model, - config: { - get: () => [], - }, }; }); From 947fac965316480e0dff8867b3daa60fa58ad120 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 13:25:16 +0100 Subject: [PATCH 10/23] test(ee): review workflows --- .../ee/server/services/__tests__/review-workflows.test.js | 3 +++ .../ee/server/services/review-workflows/review-workflows.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js b/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js index 423a9204dd..6b49a68948 100644 --- a/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js +++ b/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js @@ -71,6 +71,9 @@ const strapiMock = { return null; } }, + webhookStore: { + allowedEvents: new Map([]), + }, }; const reviewWorkflowsService = reviewWorkflowsServiceFactory({ strapi: strapiMock }); diff --git a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js index 2207f54617..7ed8f5b977 100644 --- a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js +++ b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js @@ -108,7 +108,7 @@ function persistStagesJoinTables({ strapi }) { }; } -const registerWebhookEvents = async () => +const registerWebhookEvents = async ({ strapi }) => strapi.webhookStore.allowedEvents.set('WORKFLOW_STAGE_CHANGE', 'workflow.stage.change'); module.exports = ({ strapi }) => { @@ -118,7 +118,7 @@ module.exports = ({ strapi }) => { return { async bootstrap() { await initDefaultWorkflow({ workflowsService, stagesService, strapi }); - await registerWebhookEvents(); + await registerWebhookEvents({ strapi }); }, async register() { extendReviewWorkflowContentTypes({ strapi }); From f509468158ea3e0fba5f587ffb05b93fdaf89c45 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 13:28:54 +0100 Subject: [PATCH 11/23] test(content-manager): entity manager --- .../server/services/__tests__/entity-manager.test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/core/content-manager/server/services/__tests__/entity-manager.test.js b/packages/core/content-manager/server/services/__tests__/entity-manager.test.js index 8b4b00b1a4..78aa56641a 100644 --- a/packages/core/content-manager/server/services/__tests__/entity-manager.test.js +++ b/packages/core/content-manager/server/services/__tests__/entity-manager.test.js @@ -26,6 +26,9 @@ describe('Content-Manager', () => { config: { get: (path, defaultValue) => _.get(defaultConfig, path, defaultValue), }, + webhookStore: { + allowedEvents: new Map([['ENTRY_PUBLISH', 'entry.publish']]), + }, }; entityManager = entityManagerLoader({ strapi }); }); @@ -58,6 +61,9 @@ describe('Content-Manager', () => { config: { get: (path, defaultValue) => _.get(defaultConfig, path, defaultValue), }, + webhookStore: { + allowedEvents: new Map([['ENTRY_UNPUBLISH', 'entry.unpublish']]), + }, }; entityManager = entityManagerLoader({ strapi }); }); From db767bded15683517014592de3cb97e6008a2e88 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 25 May 2023 13:30:24 +0100 Subject: [PATCH 12/23] test(upload): bootstrap --- packages/core/upload/server/__tests__/bootstrap.test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/core/upload/server/__tests__/bootstrap.test.js b/packages/core/upload/server/__tests__/bootstrap.test.js index 3c1e5400c7..7582537760 100644 --- a/packages/core/upload/server/__tests__/bootstrap.test.js +++ b/packages/core/upload/server/__tests__/bootstrap.test.js @@ -64,6 +64,9 @@ describe('Upload plugin bootstrap function', () => { set: setStore, }; }, + webhookStore: { + allowedEvents: new Map([]), + }, }; await bootstrap({ strapi }); From 5547c788eb5a72b7729e241b8cf707e0098fd5a9 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Fri, 26 May 2023 14:25:03 +0100 Subject: [PATCH 13/23] fix: register review workflow webhook events earlier in bootstrap --- .../ee/server/services/review-workflows/review-workflows.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js index 7ed8f5b977..3945ef4315 100644 --- a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js +++ b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js @@ -117,8 +117,8 @@ module.exports = ({ strapi }) => { return { async bootstrap() { - await initDefaultWorkflow({ workflowsService, stagesService, strapi }); await registerWebhookEvents({ strapi }); + await initDefaultWorkflow({ workflowsService, stagesService, strapi }); }, async register() { extendReviewWorkflowContentTypes({ strapi }); From 4020a96da6a1dc1e6c424bebbcd66bc9439de56c Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Fri, 26 May 2023 14:27:44 +0100 Subject: [PATCH 14/23] chore: separate validation between webhook service and controller --- .../core/admin/server/controllers/webhooks.js | 32 ++++++++++++++++ .../core/strapi/lib/services/webhook-store.js | 37 ++----------------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/packages/core/admin/server/controllers/webhooks.js b/packages/core/admin/server/controllers/webhooks.js index fd97f2437b..5b7804185f 100644 --- a/packages/core/admin/server/controllers/webhooks.js +++ b/packages/core/admin/server/controllers/webhooks.js @@ -1,5 +1,33 @@ 'use strict'; +const _ = require('lodash'); +const { yup, validateYupSchema } = require('@strapi/utils'); + +const urlRegex = + /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)(?:\.(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/; + +const webhookValidator = yup.object({ + name: yup.string().required(), + url: yup.string().matches(urlRegex, 'url must be a valid URL').required(), + headers: yup.lazy((data) => { + if (typeof data !== 'object') { + return yup.object().required(); + } + + return yup + .object( + _.mapValues(data, () => { + yup.string().min(1).required(); + }) + ) + .required(); + }), +}); + +const updateWebhookValidator = webhookValidator.shape({ + isEnabled: yup.boolean(), +}); + module.exports = { async listWebhooks(ctx) { const webhooks = await strapi.webhookStore.findWebhooks(); @@ -20,6 +48,8 @@ module.exports = { async createWebhook(ctx) { const { body } = ctx.request; + await validateYupSchema(webhookValidator)(body); + const webhook = await strapi.webhookStore.createWebhook(body); strapi.webhookRunner.add(webhook); @@ -31,6 +61,8 @@ module.exports = { const { id } = ctx.params; const { body } = ctx.request; + await validateYupSchema(updateWebhookValidator)(body); + const webhook = await strapi.webhookStore.findWebhook(id); if (!webhook) { diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index 90b9bf0bc5..21528fbf24 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -4,7 +4,6 @@ 'use strict'; -const { mapValues } = require('lodash/fp'); const { yup, validateYupSchema } = require('@strapi/utils'); const webhookModel = { @@ -50,36 +49,8 @@ const fromDBObject = (row) => { }; }; -const urlRegex = - /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)(?:\.(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/; - -const webhookValidator = (allowedEvents) => - yup - .object({ - name: yup.string().required(), - url: yup.string().matches(urlRegex, 'url must be a valid URL').required(), - headers: yup.lazy((data) => { - if (typeof data !== 'object') { - return yup.object().required(); - } - - return yup - .object( - mapValues(() => { - yup.string().min(1).required(); - }, data) - ) - .required(); - }), - events: yup.array().of(yup.string().oneOf(Array.from(allowedEvents.values())).required()), - }) - .noUnknown(); - -const updateWebhookValidator = (allowedEvents) => - webhookValidator(allowedEvents).shape({ - id: yup.number().required(), - isEnabled: yup.boolean().required(), - }); +const webhookEventValidator = (allowedEvents) => + yup.array().of(yup.string().oneOf(Array.from(allowedEvents.values())).required()); const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); @@ -106,7 +77,7 @@ const createWebhookStore = ({ db }) => { }, async createWebhook(data) { - await validateYupSchema(webhookValidator(allowedEvents))(data); + await validateYupSchema(webhookEventValidator(allowedEvents))(data.events); return webhookQueries .create({ @@ -116,7 +87,7 @@ const createWebhookStore = ({ db }) => { }, async updateWebhook(id, data) { - await validateYupSchema(updateWebhookValidator(allowedEvents))(data); + await validateYupSchema(webhookEventValidator(allowedEvents))(data.events); const webhook = await webhookQueries.update({ where: { id }, From 510a20c82c5960687df90f8f4f1843670502beca Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Tue, 30 May 2023 09:42:03 +0100 Subject: [PATCH 15/23] chore: update webhook event name --- .../ee/server/services/review-workflows/review-workflows.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js index 3945ef4315..d69c695605 100644 --- a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js +++ b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js @@ -109,7 +109,7 @@ function persistStagesJoinTables({ strapi }) { } const registerWebhookEvents = async ({ strapi }) => - strapi.webhookStore.allowedEvents.set('WORKFLOW_STAGE_CHANGE', 'workflow.stage.change'); + strapi.webhookStore.allowedEvents.set('WORKFLOW_UPDATE_STAGE', 'workflow.updateEntryStage'); module.exports = ({ strapi }) => { const workflowsService = getService('workflows', { strapi }); From 9f8acd2374eab7c5cd34010a082769cb7fd54279 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Tue, 30 May 2023 10:09:11 +0100 Subject: [PATCH 16/23] fix: validation in webhook service --- .../core/strapi/lib/services/webhook-store.js | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index 21528fbf24..0d532d834a 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -4,7 +4,8 @@ 'use strict'; -const { yup, validateYupSchema } = require('@strapi/utils'); +const { mapAsync } = require('@strapi/utils'); +const { ValidationError } = require('@strapi/utils').errors; const webhookModel = { uid: 'webhook', @@ -49,8 +50,18 @@ const fromDBObject = (row) => { }; }; -const webhookEventValidator = (allowedEvents) => - yup.array().of(yup.string().oneOf(Array.from(allowedEvents.values())).required()); +const webhookEventValidator = async (allowedEvents, events) => + mapAsync( + events, + (event) => { + if (Array.from(allowedEvents.values()).includes(event)) { + return; + } + + throw new ValidationError(`Webhook event ${event} is not supported`); + }, + {} + ); const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); @@ -77,7 +88,7 @@ const createWebhookStore = ({ db }) => { }, async createWebhook(data) { - await validateYupSchema(webhookEventValidator(allowedEvents))(data.events); + await webhookEventValidator(allowedEvents, data.events); return webhookQueries .create({ @@ -87,7 +98,7 @@ const createWebhookStore = ({ db }) => { }, async updateWebhook(id, data) { - await validateYupSchema(webhookEventValidator(allowedEvents))(data.events); + await webhookEventValidator(allowedEvents, data.events); const webhook = await webhookQueries.update({ where: { id }, From a43f1233d3b1f4a6842c1843c3a4d65ad81b24a6 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Tue, 30 May 2023 10:14:13 +0100 Subject: [PATCH 17/23] chore: code cleanup --- .../core/strapi/lib/services/webhook-store.js | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index 0d532d834a..08f972bf37 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -50,18 +50,17 @@ const fromDBObject = (row) => { }; }; -const webhookEventValidator = async (allowedEvents, events) => - mapAsync( - events, - (event) => { - if (Array.from(allowedEvents.values()).includes(event)) { - return; - } +const webhookEventValidator = async (allowedEvents, events) => { + const allowedValues = Array.from(allowedEvents.values()); - throw new ValidationError(`Webhook event ${event} is not supported`); - }, - {} - ); + await mapAsync(events, (event) => { + if (allowedValues.includes(event)) { + return; + } + + throw new ValidationError(`Webhook event ${event} is not supported`); + }); +}; const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); From 723187ab4c080e45a7cfde36ebdde41566725088 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Tue, 30 May 2023 11:05:42 +0100 Subject: [PATCH 18/23] chore: webhook controller yup validation --- .../core/admin/server/controllers/webhooks.js | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/core/admin/server/controllers/webhooks.js b/packages/core/admin/server/controllers/webhooks.js index 5b7804185f..e02008c233 100644 --- a/packages/core/admin/server/controllers/webhooks.js +++ b/packages/core/admin/server/controllers/webhooks.js @@ -6,23 +6,26 @@ const { yup, validateYupSchema } = require('@strapi/utils'); const urlRegex = /^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)(?:\.(?:[a-z\u00a1-\uffff0-9_]-*)*[a-z\u00a1-\uffff0-9_]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/; -const webhookValidator = yup.object({ - name: yup.string().required(), - url: yup.string().matches(urlRegex, 'url must be a valid URL').required(), - headers: yup.lazy((data) => { - if (typeof data !== 'object') { - return yup.object().required(); - } +const webhookValidator = yup + .object({ + name: yup.string().required(), + url: yup.string().matches(urlRegex, 'url must be a valid URL').required(), + headers: yup.lazy((data) => { + if (typeof data !== 'object') { + return yup.object().required(); + } - return yup - .object( - _.mapValues(data, () => { - yup.string().min(1).required(); - }) - ) - .required(); - }), -}); + return yup + .object( + _.mapValues(data, () => { + yup.string().min(1).required(); + }) + ) + .required(); + }), + events: yup.array().of(yup.string()).required(), + }) + .noUnknown(); const updateWebhookValidator = webhookValidator.shape({ isEnabled: yup.boolean(), From d77e42b8594409e466aa9be8764f305f6923727c Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Tue, 30 May 2023 16:13:10 +0100 Subject: [PATCH 19/23] refactor(webhooks): populate allowed events from relevant modules --- .../review-workflows/review-workflows.js | 2 +- .../core/content-manager/server/bootstrap.js | 5 +++ .../content-manager/server/constants/index.js | 10 ++++++ .../server/services/entity-manager.js | 14 +++++--- .../lib/services/entity-service/index.js | 33 ++++++++++++------- .../core/strapi/lib/services/webhook-store.js | 30 ++++++++--------- packages/core/upload/server/bootstrap.js | 9 +++-- packages/core/upload/server/constants.js | 7 ++++ .../core/upload/server/services/upload.js | 15 ++++++--- 9 files changed, 83 insertions(+), 42 deletions(-) create mode 100644 packages/core/content-manager/server/constants/index.js diff --git a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js index d69c695605..15b1913e5f 100644 --- a/packages/core/admin/ee/server/services/review-workflows/review-workflows.js +++ b/packages/core/admin/ee/server/services/review-workflows/review-workflows.js @@ -109,7 +109,7 @@ function persistStagesJoinTables({ strapi }) { } const registerWebhookEvents = async ({ strapi }) => - strapi.webhookStore.allowedEvents.set('WORKFLOW_UPDATE_STAGE', 'workflow.updateEntryStage'); + strapi.webhookStore.addAllowedEvent('WORKFLOW_UPDATE_STAGE', 'workflow.updateEntryStage'); module.exports = ({ strapi }) => { const workflowsService = getService('workflows', { strapi }); diff --git a/packages/core/content-manager/server/bootstrap.js b/packages/core/content-manager/server/bootstrap.js index 99768e9414..2addefd0e5 100644 --- a/packages/core/content-manager/server/bootstrap.js +++ b/packages/core/content-manager/server/bootstrap.js @@ -1,8 +1,13 @@ 'use strict'; const { getService } = require('./utils'); +const { ALLOWED_WEBHOOK_EVENTS } = require('./constants'); module.exports = async () => { + Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => { + strapi.webhookStore.addAllowedEvent(key, value); + }); + await getService('components').syncConfigurations(); await getService('content-types').syncConfigurations(); await getService('permission').registerPermissions(); diff --git a/packages/core/content-manager/server/constants/index.js b/packages/core/content-manager/server/constants/index.js new file mode 100644 index 0000000000..e0c670b0d5 --- /dev/null +++ b/packages/core/content-manager/server/constants/index.js @@ -0,0 +1,10 @@ +'use strict'; + +const ALLOWED_WEBHOOK_EVENTS = { + ENTRY_PUBLISH: 'entry.publish', + ENTRY_UNPUBLISH: 'entry.unpublish', +}; + +module.exports = { + ALLOWED_WEBHOOK_EVENTS, +}; diff --git a/packages/core/content-manager/server/services/entity-manager.js b/packages/core/content-manager/server/services/entity-manager.js index 612ab2aa4e..8886cba549 100644 --- a/packages/core/content-manager/server/services/entity-manager.js +++ b/packages/core/content-manager/server/services/entity-manager.js @@ -7,14 +7,20 @@ const { ApplicationError } = require('@strapi/utils').errors; const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate'); const { getDeepRelationsCount } = require('./utils/count'); const { sumDraftCounts } = require('./utils/draft'); +const { ALLOWED_WEBHOOK_EVENTS } = require('../constants'); const { hasDraftAndPublish } = strapiUtils.contentTypes; const { PUBLISHED_AT_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = strapiUtils.contentTypes.constants; const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE); -const emitEvent = async (event, entity, modelUid) => { - const modelDef = strapi.getModel(modelUid); +const emitEvent = async (uid, eventName, entity) => { + const event = ALLOWED_WEBHOOK_EVENTS[eventName]; + if (!event) { + throw new ApplicationError(`The webhook event ${eventName} doesn't exist.`); + } + + const modelDef = strapi.getModel(uid); const sanitizedEntity = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput( modelDef, entity @@ -253,7 +259,7 @@ module.exports = ({ strapi }) => ({ const updatedEntity = await strapi.entityService.update(uid, entity.id, params); - await emitEvent(strapi.webhookStore.allowedEvents.get('ENTRY_PUBLISH'), updatedEntity, uid); + await emitEvent(uid, 'ENTRY_PUBLISH', updatedEntity); const mappedEntity = await this.mapEntity(updatedEntity, uid); @@ -282,7 +288,7 @@ module.exports = ({ strapi }) => ({ const updatedEntity = await strapi.entityService.update(uid, entity.id, params); - await emitEvent(strapi.webhookStore.allowedEvents.get('ENTRY_UNPUBLISH'), updatedEntity, uid); + await emitEvent(uid, 'ENTRY_UNPUBLISH', updatedEntity); const mappedEntity = await this.mapEntity(updatedEntity, uid); diff --git a/packages/core/strapi/lib/services/entity-service/index.js b/packages/core/strapi/lib/services/entity-service/index.js index 7870b29ead..0373f212e6 100644 --- a/packages/core/strapi/lib/services/entity-service/index.js +++ b/packages/core/strapi/lib/services/entity-service/index.js @@ -5,7 +5,7 @@ const delegate = require('delegates'); const { InvalidTimeError, InvalidDateError, InvalidDateTimeError, InvalidRelationError } = require('@strapi/database').errors; const { contentTypes: contentTypesUtils, sanitize } = require('@strapi/utils'); -const { ValidationError } = require('@strapi/utils').errors; +const { ValidationError, ApplicationError } = require('@strapi/utils').errors; const { isAnyToMany } = require('@strapi/utils').relations; const { transformParamsToQuery } = require('@strapi/utils').convertQueryParams; const uploadFiles = require('../utils/upload-files'); @@ -42,6 +42,12 @@ const updatePipeline = (data, context) => { return applyTransforms(data, context); }; +const ALLOWED_WEBHOOK_EVENTS = { + ENTRY_CREATE: 'entry.create', + ENTRY_UPDATE: 'entry.update', + ENTRY_DELETE: 'entry.delete', +}; + /** * @type {import('.').default} */ @@ -56,7 +62,12 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) return result; }, - async emitEvent(uid, event, entity) { + async emitEvent(uid, eventName, entity) { + const event = ALLOWED_WEBHOOK_EVENTS[eventName]; + if (!event) { + throw new ApplicationError(`The webhook event ${eventName} is not supported.`); + } + // Ignore audit log events to prevent infinite loops if (uid === 'admin::audit-log') { return; @@ -173,7 +184,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entity = await this.wrapResult(entity, { uid, action: 'create' }); - await this.emitEvent(uid, strapi.webhookStore.allowedEvents.get('ENTRY_CREATE'), entity); + await this.emitEvent(uid, 'ENTRY_CREATE', entity); return entity; }, @@ -226,7 +237,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entity = await this.wrapResult(entity, { uid, action: 'update' }); - await this.emitEvent(uid, strapi.webhookStore.allowedEvents.get('ENTRY_UPDATE'), entity); + await this.emitEvent(uid, 'ENTRY_UPDATE', entity); return entity; }, @@ -253,11 +264,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entityToDelete = await this.wrapResult(entityToDelete, { uid, action: 'delete' }); - await this.emitEvent( - uid, - strapi.webhookStore.allowedEvents.get('ENTRY_DELETE'), - entityToDelete - ); + await this.emitEvent(uid, 'ENTRY_DELETE', entityToDelete); return entityToDelete; }, @@ -288,9 +295,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) // Trigger webhooks. One for each entity await Promise.all( - entitiesToDelete.map((entity) => - this.emitEvent(uid, strapi.webhookStore.allowedEvents.get('ENTRY_DELETE'), entity) - ) + entitiesToDelete.map((entity) => this.emitEvent(uid, 'ENTRY_DELETE', entity)) ); return deletedEntities; @@ -332,6 +337,10 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) }); module.exports = (ctx) => { + Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => { + strapi.webhookStore.addAllowedEvent(key, value); + }); + const implementation = createDefaultImplementation(ctx); const service = { diff --git a/packages/core/strapi/lib/services/webhook-store.js b/packages/core/strapi/lib/services/webhook-store.js index 08f972bf37..f46cfdb7e7 100644 --- a/packages/core/strapi/lib/services/webhook-store.js +++ b/packages/core/strapi/lib/services/webhook-store.js @@ -65,29 +65,31 @@ const webhookEventValidator = async (allowedEvents, events) => { const createWebhookStore = ({ db }) => { const webhookQueries = db.query('webhook'); - const allowedEvents = new Map([ - ['ENTRY_CREATE', 'entry.create'], - ['ENTRY_UPDATE', 'entry.update'], - ['ENTRY_DELETE', 'entry.delete'], - ['ENTRY_PUBLISH', 'entry.publish'], - ['ENTRY_UNPUBLISH', 'entry.unpublish'], - ]); - return { - allowedEvents, + allowedEvents: new Map([]), + addAllowedEvent(key, value) { + this.allowedEvents.set(key, value); + }, + removeAllowedEvent(key) { + this.allowedEvents.delete(key); + }, + listAllowedEvents() { + return Array.from(this.allowedEvents.keys()); + }, + getAllowedEvent(key) { + return this.allowedEvents.get(key); + }, async findWebhooks() { const results = await webhookQueries.findMany(); return results.map(fromDBObject); }, - async findWebhook(id) { const result = await webhookQueries.findOne({ where: { id } }); return result ? fromDBObject(result) : null; }, - async createWebhook(data) { - await webhookEventValidator(allowedEvents, data.events); + await webhookEventValidator(this.allowedEvents, data.events); return webhookQueries .create({ @@ -95,9 +97,8 @@ const createWebhookStore = ({ db }) => { }) .then(fromDBObject); }, - async updateWebhook(id, data) { - await webhookEventValidator(allowedEvents, data.events); + await webhookEventValidator(this.allowedEvents, data.events); const webhook = await webhookQueries.update({ where: { id }, @@ -106,7 +107,6 @@ const createWebhookStore = ({ db }) => { return webhook ? fromDBObject(webhook) : null; }, - async deleteWebhook(id) { const webhook = await webhookQueries.delete({ where: { id } }); return webhook ? fromDBObject(webhook) : null; diff --git a/packages/core/upload/server/bootstrap.js b/packages/core/upload/server/bootstrap.js index 1cca42014a..1498bc4f2e 100644 --- a/packages/core/upload/server/bootstrap.js +++ b/packages/core/upload/server/bootstrap.js @@ -1,7 +1,7 @@ 'use strict'; const { getService } = require('./utils'); -const { ALLOWED_SORT_STRINGS } = require('./constants'); +const { ALLOWED_SORT_STRINGS, ALLOWED_WEBHOOK_EVENTS } = require('./constants'); module.exports = async ({ strapi }) => { const defaultConfig = { @@ -49,10 +49,9 @@ module.exports = async ({ strapi }) => { }; const registerWebhookEvents = async () => - strapi.webhookStore.allowedEvents - .set('MEDIA_CREATE', 'media.create') - .set('MEDIA_UPDATE', 'media.update') - .set('MEDIA_DELETE', 'media.delete'); + Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => { + strapi.webhookStore.addAllowedEvent(key, value); + }); const registerPermissionActions = async () => { const actions = [ diff --git a/packages/core/upload/server/constants.js b/packages/core/upload/server/constants.js index 3ff08343b0..be25bf1a5a 100644 --- a/packages/core/upload/server/constants.js +++ b/packages/core/upload/server/constants.js @@ -19,10 +19,17 @@ const ALLOWED_SORT_STRINGS = [ 'updatedAt:ASC', ]; +const ALLOWED_WEBHOOK_EVENTS = { + MEDIA_CREATE: 'media.create', + MEDIA_UPDATE: 'media.update', + MEDIA_DELETE: 'media.delete', +}; + module.exports = { ACTIONS, FOLDER_MODEL_UID: 'plugin::upload.folder', FILE_MODEL_UID: 'plugin::upload.file', API_UPLOAD_FOLDER_BASE_NAME: 'API Uploads', ALLOWED_SORT_STRINGS, + ALLOWED_WEBHOOK_EVENTS, }; diff --git a/packages/core/upload/server/services/upload.js b/packages/core/upload/server/services/upload.js index 1297a5646f..3118f3484c 100644 --- a/packages/core/upload/server/services/upload.js +++ b/packages/core/upload/server/services/upload.js @@ -21,7 +21,7 @@ const { file: { bytesToKbytes }, } = require('@strapi/utils'); -const { FILE_MODEL_UID } = require('../constants'); +const { FILE_MODEL_UID, ALLOWED_WEBHOOK_EVENTS } = require('../constants'); const { getService } = require('../utils'); const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants; @@ -59,7 +59,12 @@ const createAndAssignTmpWorkingDirectoryToFiles = async (files) => { }; module.exports = ({ strapi }) => ({ - async emitEvent(event, data) { + async emitEvent(eventName, data) { + const event = ALLOWED_WEBHOOK_EVENTS[eventName]; + if (!event) { + throw new ApplicationError(`The webhook event ${eventName} doesn't exist.`); + } + const modelDef = strapi.getModel(FILE_MODEL_UID); const sanitizedData = await sanitize.sanitizers.defaultSanitizeOutput(modelDef, data); @@ -344,7 +349,7 @@ module.exports = ({ strapi }) => ({ const res = await strapi.entityService.update(FILE_MODEL_UID, id, { data: fileValues }); - await this.emitEvent(strapi.webhookStore.allowedEvents.get('MEDIA_UPDATE'), res); + await this.emitEvent('MEDIA_UPDATE', res); return res; }, @@ -360,7 +365,7 @@ module.exports = ({ strapi }) => ({ const res = await strapi.query(FILE_MODEL_UID).create({ data: fileValues }); - await this.emitEvent(strapi.webhookStore.allowedEvents.get('MEDIA_CREATE'), res); + await this.emitEvent('MEDIA_CREATE', res); return res; }, @@ -397,7 +402,7 @@ module.exports = ({ strapi }) => ({ where: { id: file.id }, }); - await this.emitEvent(strapi.webhookStore.allowedEvents.get('MEDIA_DELETE'), media); + await this.emitEvent('MEDIA_DELETE', media); return strapi.query(FILE_MODEL_UID).delete({ where: { id: file.id } }); }, From e87dad437304dc13d9b00a9d081e92f4bb878e32 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Tue, 30 May 2023 16:15:05 +0100 Subject: [PATCH 20/23] chore: linter rule --- packages/core/admin/server/controllers/webhooks.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/core/admin/server/controllers/webhooks.js b/packages/core/admin/server/controllers/webhooks.js index e02008c233..147f3f8318 100644 --- a/packages/core/admin/server/controllers/webhooks.js +++ b/packages/core/admin/server/controllers/webhooks.js @@ -111,11 +111,10 @@ module.exports = { for (const id of ids) { const webhook = await strapi.webhookStore.findWebhook(id); - // eslint-disable-next-line no-continue - if (!webhook) continue; - - await strapi.webhookStore.deleteWebhook(id); - strapi.webhookRunner.remove(webhook); + if (webhook) { + await strapi.webhookStore.deleteWebhook(id); + strapi.webhookRunner.remove(webhook); + } } ctx.send({ data: ids }); From 9ecb8fe34ca118ed60e406ac5c1a935058202707 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Wed, 31 May 2023 14:28:32 +0100 Subject: [PATCH 21/23] test: fix tests for addAllowedEvent --- .../__tests__/review-workflows.test.js | 2 +- .../__tests__/entity-service-events.test.js | 8 +------ .../__tests__/entity-service.test.js | 21 +++++++++++-------- .../lib/services/entity-service/index.js | 2 +- .../upload/server/__tests__/bootstrap.test.js | 2 +- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js b/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js index 6b49a68948..4ab1152f34 100644 --- a/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js +++ b/packages/core/admin/ee/server/services/__tests__/review-workflows.test.js @@ -72,7 +72,7 @@ const strapiMock = { } }, webhookStore: { - allowedEvents: new Map([]), + addAllowedEvent: jest.fn(), }, }; diff --git a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js index d27aeac830..2b63fe9d1e 100644 --- a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js +++ b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service-events.test.js @@ -22,13 +22,7 @@ describe('Entity service triggers webhooks', () => { strapi: { getModel: () => model, webhookStore: { - allowedEvents: new Map([ - ['ENTRY_CREATE', 'entry.create'], - ['ENTRY_UPDATE', 'entry.update'], - ['ENTRY_DELETE', 'entry.delete'], - ['ENTRY_PUBLISH', 'entry.publish'], - ['ENTRY_UNPUBLISH', 'entry.unpublish'], - ]), + addAllowedEvent: jest.fn(), }, }, db: { diff --git a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js index 4167493f67..534784ba31 100644 --- a/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js +++ b/packages/core/strapi/lib/services/entity-service/__tests__/entity-service.test.js @@ -18,20 +18,18 @@ describe('Entity service', () => { }, }, query: jest.fn(() => ({})), - }; - - beforeEach(() => { - global.strapi.webhookStore = { + webhookStore: { allowedEvents: new Map([['ENTRY_CREATE', 'entry.create']]), - }; - }); + addAllowedEvent: jest.fn(), + }, + }; describe('Decorator', () => { test.each(['create', 'update', 'findMany', 'findOne', 'delete', 'count', 'findPage'])( 'Can decorate', async (method) => { const instance = createEntityService({ - strapi: {}, + strapi: global.strapi, db: {}, eventHub: new EventEmitter(), }); @@ -67,6 +65,7 @@ describe('Entity service', () => { }; const fakeStrapi = { + ...global.strapi, getModel: jest.fn(() => { return { kind: 'singleType', privateAttributes: [] }; }), @@ -383,10 +382,13 @@ describe('Entity service', () => { }, }; - global.strapi = fakeStrapi; + global.strapi = { + ...global.strapi, + ...fakeStrapi, + }; instance = createEntityService({ - strapi: fakeStrapi, + strapi: global.strapi, db: fakeDB, eventHub: new EventEmitter(), entityValidator, @@ -533,6 +535,7 @@ describe('Entity service', () => { }; global.strapi = { + ...global.strapi, getModel: jest.fn((uid) => { return fakeModels[uid]; }), diff --git a/packages/core/strapi/lib/services/entity-service/index.js b/packages/core/strapi/lib/services/entity-service/index.js index 0373f212e6..809431eed4 100644 --- a/packages/core/strapi/lib/services/entity-service/index.js +++ b/packages/core/strapi/lib/services/entity-service/index.js @@ -338,7 +338,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) module.exports = (ctx) => { Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => { - strapi.webhookStore.addAllowedEvent(key, value); + ctx.strapi.webhookStore.addAllowedEvent(key, value); }); const implementation = createDefaultImplementation(ctx); diff --git a/packages/core/upload/server/__tests__/bootstrap.test.js b/packages/core/upload/server/__tests__/bootstrap.test.js index 3dd20433f5..aa80bfcb6a 100644 --- a/packages/core/upload/server/__tests__/bootstrap.test.js +++ b/packages/core/upload/server/__tests__/bootstrap.test.js @@ -69,7 +69,7 @@ describe('Upload plugin bootstrap function', () => { }; }, webhookStore: { - allowedEvents: new Map([]), + addAllowedEvent: jest.fn(), }, }; From c53b7a7ae4999a6afae158d3b68b0633efc3873f Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 1 Jun 2023 15:20:13 +0100 Subject: [PATCH 22/23] fix: simplify emitEvent logic --- .../server/services/entity-manager.js | 15 +++++------- .../lib/services/entity-service/index.js | 23 ++++++++----------- .../core/upload/server/services/upload.js | 18 +++++++-------- 3 files changed, 24 insertions(+), 32 deletions(-) diff --git a/packages/core/content-manager/server/services/entity-manager.js b/packages/core/content-manager/server/services/entity-manager.js index 8886cba549..87224dbb14 100644 --- a/packages/core/content-manager/server/services/entity-manager.js +++ b/packages/core/content-manager/server/services/entity-manager.js @@ -7,19 +7,16 @@ const { ApplicationError } = require('@strapi/utils').errors; const { getDeepPopulate, getDeepPopulateDraftCount } = require('./utils/populate'); const { getDeepRelationsCount } = require('./utils/count'); const { sumDraftCounts } = require('./utils/draft'); -const { ALLOWED_WEBHOOK_EVENTS } = require('../constants'); +const { + ALLOWED_WEBHOOK_EVENTS: { ENTRY_PUBLISH, ENTRY_UNPUBLISH }, +} = require('../constants'); const { hasDraftAndPublish } = strapiUtils.contentTypes; const { PUBLISHED_AT_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = strapiUtils.contentTypes.constants; const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE); -const emitEvent = async (uid, eventName, entity) => { - const event = ALLOWED_WEBHOOK_EVENTS[eventName]; - if (!event) { - throw new ApplicationError(`The webhook event ${eventName} doesn't exist.`); - } - +const emitEvent = async (uid, event, entity) => { const modelDef = strapi.getModel(uid); const sanitizedEntity = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput( modelDef, @@ -259,7 +256,7 @@ module.exports = ({ strapi }) => ({ const updatedEntity = await strapi.entityService.update(uid, entity.id, params); - await emitEvent(uid, 'ENTRY_PUBLISH', updatedEntity); + await emitEvent(uid, ENTRY_PUBLISH, updatedEntity); const mappedEntity = await this.mapEntity(updatedEntity, uid); @@ -288,7 +285,7 @@ module.exports = ({ strapi }) => ({ const updatedEntity = await strapi.entityService.update(uid, entity.id, params); - await emitEvent(uid, 'ENTRY_UNPUBLISH', updatedEntity); + await emitEvent(uid, ENTRY_UNPUBLISH, updatedEntity); const mappedEntity = await this.mapEntity(updatedEntity, uid); diff --git a/packages/core/strapi/lib/services/entity-service/index.js b/packages/core/strapi/lib/services/entity-service/index.js index 809431eed4..a12e991b4e 100644 --- a/packages/core/strapi/lib/services/entity-service/index.js +++ b/packages/core/strapi/lib/services/entity-service/index.js @@ -5,7 +5,7 @@ const delegate = require('delegates'); const { InvalidTimeError, InvalidDateError, InvalidDateTimeError, InvalidRelationError } = require('@strapi/database').errors; const { contentTypes: contentTypesUtils, sanitize } = require('@strapi/utils'); -const { ValidationError, ApplicationError } = require('@strapi/utils').errors; +const { ValidationError } = require('@strapi/utils').errors; const { isAnyToMany } = require('@strapi/utils').relations; const { transformParamsToQuery } = require('@strapi/utils').convertQueryParams; const uploadFiles = require('../utils/upload-files'); @@ -62,12 +62,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) return result; }, - async emitEvent(uid, eventName, entity) { - const event = ALLOWED_WEBHOOK_EVENTS[eventName]; - if (!event) { - throw new ApplicationError(`The webhook event ${eventName} is not supported.`); - } - + async emitEvent(uid, event, entity) { // Ignore audit log events to prevent infinite loops if (uid === 'admin::audit-log') { return; @@ -184,7 +179,8 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entity = await this.wrapResult(entity, { uid, action: 'create' }); - await this.emitEvent(uid, 'ENTRY_CREATE', entity); + const { ENTRY_CREATE } = ALLOWED_WEBHOOK_EVENTS; + await this.emitEvent(uid, ENTRY_CREATE, entity); return entity; }, @@ -237,7 +233,8 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entity = await this.wrapResult(entity, { uid, action: 'update' }); - await this.emitEvent(uid, 'ENTRY_UPDATE', entity); + const { ENTRY_UPDATE } = ALLOWED_WEBHOOK_EVENTS; + await this.emitEvent(uid, ENTRY_UPDATE, entity); return entity; }, @@ -264,7 +261,8 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entityToDelete = await this.wrapResult(entityToDelete, { uid, action: 'delete' }); - await this.emitEvent(uid, 'ENTRY_DELETE', entityToDelete); + const { ENTRY_DELETE } = ALLOWED_WEBHOOK_EVENTS; + await this.emitEvent(uid, ENTRY_DELETE, entityToDelete); return entityToDelete; }, @@ -294,9 +292,8 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator }) entitiesToDelete = await this.wrapResult(entitiesToDelete, { uid, action: 'delete' }); // Trigger webhooks. One for each entity - await Promise.all( - entitiesToDelete.map((entity) => this.emitEvent(uid, 'ENTRY_DELETE', entity)) - ); + const { ENTRY_DELETE } = ALLOWED_WEBHOOK_EVENTS; + await Promise.all(entitiesToDelete.map((entity) => this.emitEvent(uid, ENTRY_DELETE, entity))); return deletedEntities; }, diff --git a/packages/core/upload/server/services/upload.js b/packages/core/upload/server/services/upload.js index 3118f3484c..5ef37c89aa 100644 --- a/packages/core/upload/server/services/upload.js +++ b/packages/core/upload/server/services/upload.js @@ -21,7 +21,10 @@ const { file: { bytesToKbytes }, } = require('@strapi/utils'); -const { FILE_MODEL_UID, ALLOWED_WEBHOOK_EVENTS } = require('../constants'); +const { + FILE_MODEL_UID, + ALLOWED_WEBHOOK_EVENTS: { MEDIA_CREATE, MEDIA_UPDATE, MEDIA_DELETE }, +} = require('../constants'); const { getService } = require('../utils'); const { UPDATED_BY_ATTRIBUTE, CREATED_BY_ATTRIBUTE } = contentTypesUtils.constants; @@ -59,12 +62,7 @@ const createAndAssignTmpWorkingDirectoryToFiles = async (files) => { }; module.exports = ({ strapi }) => ({ - async emitEvent(eventName, data) { - const event = ALLOWED_WEBHOOK_EVENTS[eventName]; - if (!event) { - throw new ApplicationError(`The webhook event ${eventName} doesn't exist.`); - } - + async emitEvent(event, data) { const modelDef = strapi.getModel(FILE_MODEL_UID); const sanitizedData = await sanitize.sanitizers.defaultSanitizeOutput(modelDef, data); @@ -349,7 +347,7 @@ module.exports = ({ strapi }) => ({ const res = await strapi.entityService.update(FILE_MODEL_UID, id, { data: fileValues }); - await this.emitEvent('MEDIA_UPDATE', res); + await this.emitEvent(MEDIA_UPDATE, res); return res; }, @@ -365,7 +363,7 @@ module.exports = ({ strapi }) => ({ const res = await strapi.query(FILE_MODEL_UID).create({ data: fileValues }); - await this.emitEvent('MEDIA_CREATE', res); + await this.emitEvent(MEDIA_CREATE, res); return res; }, @@ -402,7 +400,7 @@ module.exports = ({ strapi }) => ({ where: { id: file.id }, }); - await this.emitEvent('MEDIA_DELETE', media); + await this.emitEvent(MEDIA_DELETE, media); return strapi.query(FILE_MODEL_UID).delete({ where: { id: file.id } }); }, From 59d61424a90debcf2c0cc5a9d1fbf7dd0daad651 Mon Sep 17 00:00:00 2001 From: Jamie Howard Date: Thu, 1 Jun 2023 15:21:47 +0100 Subject: [PATCH 23/23] chore: fix entity manager linter warnings --- .../server/controllers/__tests__/single-types.test.js | 4 ++-- .../server/controllers/collection-types.js | 8 ++++---- .../content-manager/server/controllers/single-types.js | 8 ++++---- .../server/services/__tests__/entity-manager.test.js | 4 ++-- .../content-manager/server/services/entity-manager.js | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/core/content-manager/server/controllers/__tests__/single-types.test.js b/packages/core/content-manager/server/controllers/__tests__/single-types.test.js index 6915ec2d6e..8cc8d5602a 100644 --- a/packages/core/content-manager/server/controllers/__tests__/single-types.test.js +++ b/packages/core/content-manager/server/controllers/__tests__/single-types.test.js @@ -382,7 +382,7 @@ describe('Single Types', () => { await singleTypes.publish(ctx); - expect(publishFn).toHaveBeenCalledWith(entity, { updatedBy: state.user.id }, modelUid); + expect(publishFn).toHaveBeenCalledWith(entity, modelUid, { updatedBy: state.user.id }); expect(permissionChecker.cannot.publish).toHaveBeenCalledWith(entity); expect(permissionChecker.sanitizeOutput).toHaveBeenCalled(); }); @@ -479,7 +479,7 @@ describe('Single Types', () => { await singleTypes.unpublish(ctx); - expect(unpublishFn).toHaveBeenCalledWith(entity, { updatedBy: state.user.id }, modelUid); + expect(unpublishFn).toHaveBeenCalledWith(entity, modelUid, { updatedBy: state.user.id }); expect(permissionChecker.cannot.unpublish).toHaveBeenCalledWith(entity); expect(permissionChecker.sanitizeOutput).toHaveBeenCalled(); }); diff --git a/packages/core/content-manager/server/controllers/collection-types.js b/packages/core/content-manager/server/controllers/collection-types.js index 0bfb296d46..bfad9f6656 100644 --- a/packages/core/content-manager/server/controllers/collection-types.js +++ b/packages/core/content-manager/server/controllers/collection-types.js @@ -174,8 +174,8 @@ module.exports = { const result = await entityManager.publish( entity, - setCreatorFields({ user, isEdition: true })({}), - model + model, + setCreatorFields({ user, isEdition: true })({}) ); ctx.body = await permissionChecker.sanitizeOutput(result); @@ -204,8 +204,8 @@ module.exports = { const result = await entityManager.unpublish( entity, - setCreatorFields({ user, isEdition: true })({}), - model + model, + setCreatorFields({ user, isEdition: true })({}) ); ctx.body = await permissionChecker.sanitizeOutput(result); diff --git a/packages/core/content-manager/server/controllers/single-types.js b/packages/core/content-manager/server/controllers/single-types.js index a46e40cb8e..bee0f11076 100644 --- a/packages/core/content-manager/server/controllers/single-types.js +++ b/packages/core/content-manager/server/controllers/single-types.js @@ -148,8 +148,8 @@ module.exports = { const publishedEntity = await entityManager.publish( entity, - setCreatorFields({ user, isEdition: true })({}), - model + model, + setCreatorFields({ user, isEdition: true })({}) ); ctx.body = await permissionChecker.sanitizeOutput(publishedEntity); @@ -181,8 +181,8 @@ module.exports = { const unpublishedEntity = await entityManager.unpublish( entity, - setCreatorFields({ user, isEdition: true })({}), - model + model, + setCreatorFields({ user, isEdition: true })({}) ); ctx.body = await permissionChecker.sanitizeOutput(unpublishedEntity); diff --git a/packages/core/content-manager/server/services/__tests__/entity-manager.test.js b/packages/core/content-manager/server/services/__tests__/entity-manager.test.js index 78aa56641a..87ca90aeb0 100644 --- a/packages/core/content-manager/server/services/__tests__/entity-manager.test.js +++ b/packages/core/content-manager/server/services/__tests__/entity-manager.test.js @@ -40,7 +40,7 @@ describe('Content-Manager', () => { test('Publish a content-type', async () => { const uid = 'api::test.test'; const entity = { id: 1, publishedAt: null }; - await entityManager.publish(entity, {}, uid); + await entityManager.publish(entity, uid, {}); expect(strapi.entityService.update).toBeCalledWith(uid, entity.id, { data: { publishedAt: expect.any(Date) }, @@ -75,7 +75,7 @@ describe('Content-Manager', () => { test('Unpublish a content-type', async () => { const uid = 'api::test.test'; const entity = { id: 1, publishedAt: new Date() }; - await entityManager.unpublish(entity, {}, uid); + await entityManager.unpublish(entity, uid, {}); expect(strapi.entityService.update).toHaveBeenCalledWith(uid, entity.id, { data: { publishedAt: null }, diff --git a/packages/core/content-manager/server/services/entity-manager.js b/packages/core/content-manager/server/services/entity-manager.js index 87224dbb14..800e1b2760 100644 --- a/packages/core/content-manager/server/services/entity-manager.js +++ b/packages/core/content-manager/server/services/entity-manager.js @@ -231,7 +231,7 @@ module.exports = ({ strapi }) => ({ return strapi.entityService.deleteMany(uid, params); }, - async publish(entity, body = {}, uid) { + async publish(entity, uid, body = {}) { if (entity[PUBLISHED_AT_ATTRIBUTE]) { throw new ApplicationError('already.published'); } @@ -268,7 +268,7 @@ module.exports = ({ strapi }) => ({ return mappedEntity; }, - async unpublish(entity, body = {}, uid) { + async unpublish(entity, uid, body = {}) { if (!entity[PUBLISHED_AT_ATTRIBUTE]) { throw new ApplicationError('already.draft'); }