@strapi/content-releases 5.52.2 → 5.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/admin/pages/ReleasesPage.js +0 -1
  2. package/dist/admin/pages/ReleasesPage.js.map +1 -1
  3. package/dist/admin/pages/ReleasesPage.mjs +0 -1
  4. package/dist/admin/pages/ReleasesPage.mjs.map +1 -1
  5. package/dist/server/audit-logs.js +182 -0
  6. package/dist/server/audit-logs.js.map +1 -0
  7. package/dist/server/audit-logs.mjs +175 -0
  8. package/dist/server/audit-logs.mjs.map +1 -0
  9. package/dist/server/bootstrap.js +6 -0
  10. package/dist/server/bootstrap.js.map +1 -1
  11. package/dist/server/bootstrap.mjs +6 -0
  12. package/dist/server/bootstrap.mjs.map +1 -1
  13. package/dist/server/constants.js +20 -0
  14. package/dist/server/constants.js.map +1 -1
  15. package/dist/server/constants.mjs +20 -1
  16. package/dist/server/constants.mjs.map +1 -1
  17. package/dist/server/controllers/release.js +9 -21
  18. package/dist/server/controllers/release.js.map +1 -1
  19. package/dist/server/controllers/release.mjs +9 -21
  20. package/dist/server/controllers/release.mjs.map +1 -1
  21. package/dist/server/controllers/settings.js +0 -1
  22. package/dist/server/controllers/settings.js.map +1 -1
  23. package/dist/server/controllers/settings.mjs +0 -1
  24. package/dist/server/controllers/settings.mjs.map +1 -1
  25. package/dist/server/services/release-action.js +59 -3
  26. package/dist/server/services/release-action.js.map +1 -1
  27. package/dist/server/services/release-action.mjs +60 -4
  28. package/dist/server/services/release-action.mjs.map +1 -1
  29. package/dist/server/services/release.js +110 -10
  30. package/dist/server/services/release.js.map +1 -1
  31. package/dist/server/services/release.mjs +111 -11
  32. package/dist/server/services/release.mjs.map +1 -1
  33. package/dist/server/services/scheduling.js +7 -2
  34. package/dist/server/services/scheduling.js.map +1 -1
  35. package/dist/server/services/scheduling.mjs +7 -2
  36. package/dist/server/services/scheduling.mjs.map +1 -1
  37. package/dist/server/services/settings.js +29 -1
  38. package/dist/server/services/settings.js.map +1 -1
  39. package/dist/server/services/settings.mjs +29 -1
  40. package/dist/server/services/settings.mjs.map +1 -1
  41. package/dist/server/src/audit-logs.d.ts +99 -0
  42. package/dist/server/src/audit-logs.d.ts.map +1 -0
  43. package/dist/server/src/bootstrap.d.ts.map +1 -1
  44. package/dist/server/src/constants.d.ts +20 -0
  45. package/dist/server/src/constants.d.ts.map +1 -1
  46. package/dist/server/src/controllers/release.d.ts.map +1 -1
  47. package/dist/server/src/index.d.ts +8 -1
  48. package/dist/server/src/index.d.ts.map +1 -1
  49. package/dist/server/src/services/index.d.ts +8 -1
  50. package/dist/server/src/services/index.d.ts.map +1 -1
  51. package/dist/server/src/services/release-action.d.ts.map +1 -1
  52. package/dist/server/src/services/release.d.ts +8 -1
  53. package/dist/server/src/services/release.d.ts.map +1 -1
  54. package/dist/server/src/services/scheduling.d.ts.map +1 -1
  55. package/dist/server/src/services/settings.d.ts.map +1 -1
  56. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"release-action.mjs","sources":["../../../server/src/services/release-action.ts"],"sourcesContent":["import { errors, async } from '@strapi/utils';\n\nimport type { Core, Internal, Modules, UID, Data } from '@strapi/types';\n\nimport _ from 'lodash/fp';\n\nimport { RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';\n\nimport type {\n CreateReleaseAction,\n GetReleaseActions,\n ReleaseAction,\n ReleaseActionGroupBy,\n UpdateReleaseAction,\n DeleteReleaseAction,\n} from '../../../shared/contracts/release-actions';\nimport type { Entity } from '../../../shared/types';\nimport { getService, getDraftEntryValidStatus, getEntry, getEntryStatus } from '../utils';\n\nconst getGroupName = (queryValue: string) => {\n switch (queryValue) {\n case 'contentType':\n return 'contentType.displayName';\n case 'type':\n return 'type';\n case 'locale':\n return _.getOr('No locale', 'locale.name');\n default:\n return 'contentType.displayName';\n }\n};\n\nexport interface Locale extends Entity {\n name: string;\n code: string;\n}\n\ntype LocaleDictionary = {\n [key: Locale['code']]: Pick<Locale, 'name' | 'code'>;\n};\n\nconst createReleaseActionService = ({ strapi }: { strapi: Core.Strapi }) => {\n const getLocalesDataForActions = async () => {\n if (!strapi.plugin('i18n')) {\n return {};\n }\n\n const allLocales: Locale[] = (await strapi.plugin('i18n').service('locales').find()) || [];\n return allLocales.reduce<LocaleDictionary>((acc, locale) => {\n acc[locale.code] = { name: locale.name, code: locale.code };\n\n return acc;\n }, {});\n };\n\n const getContentTypesDataForActions = async (\n contentTypesUids: ReleaseAction['contentType'][]\n ) => {\n const contentManagerContentTypeService = strapi\n .plugin('content-manager')\n .service('content-types');\n\n const contentTypesData: Record<\n Internal.UID.ContentType,\n { mainField: string; displayName: string }\n > = {};\n for (const contentTypeUid of contentTypesUids) {\n const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({\n uid: contentTypeUid,\n });\n\n contentTypesData[contentTypeUid] = {\n mainField: contentTypeConfig.settings.mainField,\n displayName: strapi.getModel(contentTypeUid).info.displayName,\n };\n }\n\n return contentTypesData;\n };\n\n return {\n async create(\n releaseId: CreateReleaseAction.Request['params']['releaseId'],\n action: CreateReleaseAction.Request['body'],\n { disableUpdateReleaseStatus = false }: { disableUpdateReleaseStatus?: boolean } = {}\n ) {\n const { validateEntryData, validateUniqueEntry } = getService('release-validation', {\n strapi,\n });\n\n await Promise.all([\n validateEntryData(action.contentType, action.entryDocumentId),\n validateUniqueEntry(releaseId, action),\n ]);\n\n // If we are adding a singleType, we need to append the documentId of that singleType\n const model = strapi.contentType(action.contentType);\n if (model.kind === 'singleType') {\n const document = await strapi.db.query(model.uid).findOne({ select: ['documentId'] });\n\n if (!document) {\n throw new errors.NotFoundError(`No entry found for contentType ${action.contentType}`);\n }\n\n action.entryDocumentId = document.documentId;\n }\n\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // If the action is a publish, check if the entry is valid\n // If the action is an unpublish, skip the validation\n const actionStatus =\n action.type === 'publish'\n ? await getDraftEntryValidStatus(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n },\n {\n strapi,\n }\n )\n : true;\n\n const releaseAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).create({\n data: {\n ...action,\n release: release.id,\n isEntryValid: actionStatus,\n },\n populate: { release: { select: ['id'] } },\n });\n\n if (!disableUpdateReleaseStatus) {\n getService('release', { strapi }).updateReleaseStatus(release.id);\n }\n\n return releaseAction;\n },\n\n async findPage(\n releaseId: GetReleaseActions.Request['params']['releaseId'],\n query?: GetReleaseActions.Request['query']\n ) {\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n select: ['id'],\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const dbQuery = strapi.get('query-params').transform(RELEASE_ACTION_MODEL_UID, query ?? {});\n const { results: actions, pagination } = await strapi.db\n .query(RELEASE_ACTION_MODEL_UID)\n .findPage({\n ...dbQuery,\n where: {\n release: releaseId,\n },\n });\n\n // For each contentType on the release, we create a custom populate object for nested relations\n const populateBuilderService = strapi.plugin('content-manager').service('populate-builder');\n\n const actionsWithEntry = await async.map(actions, async (action: ReleaseAction) => {\n // @ts-expect-error - Core.Service type is not a function\n const populate = await populateBuilderService(action.contentType)\n .populateDeep(Infinity)\n .build();\n\n const entry = await getEntry(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n populate,\n status: action.type === 'publish' ? 'draft' : 'published',\n },\n { strapi }\n );\n\n return {\n ...action,\n entry,\n status: entry ? await getEntryStatus(action.contentType, entry) : null,\n };\n });\n\n return {\n results: actionsWithEntry,\n pagination,\n };\n },\n\n async groupActions(actions: ReleaseAction[], groupBy: ReleaseActionGroupBy) {\n const contentTypeUids = actions.reduce<ReleaseAction['contentType'][]>((acc, action) => {\n if (!acc.includes(action.contentType)) {\n acc.push(action.contentType);\n }\n\n return acc;\n }, []);\n const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);\n const allLocalesDictionary = await getLocalesDataForActions();\n\n const formattedData = actions.map((action: ReleaseAction) => {\n const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];\n\n return {\n ...action,\n locale: action.locale ? allLocalesDictionary[action.locale] : null,\n contentType: {\n displayName,\n mainFieldValue: action.entry[mainField],\n uid: action.contentType,\n },\n };\n });\n\n const groupName = getGroupName(groupBy);\n return _.groupBy(groupName)(formattedData);\n },\n\n async getContentTypeModelsFromActions(actions: ReleaseAction[]) {\n const contentTypeUids = actions.reduce<ReleaseAction['contentType'][]>((acc, action) => {\n if (!acc.includes(action.contentType)) {\n acc.push(action.contentType);\n }\n\n return acc;\n }, []);\n\n const workflowsService = strapi.plugin('review-workflows').service('workflows');\n\n const contentTypeModelsMap = await async.reduce(contentTypeUids)(\n async (\n accPromise: Promise<GetReleaseActions.Response['meta']['contentTypes']>,\n contentTypeUid: ReleaseAction['contentType']\n ) => {\n const acc = await accPromise;\n const contentTypeModel = strapi.getModel(contentTypeUid);\n\n // Workflows service may not be available depending on the license\n const workflow = await workflowsService?.getAssignedWorkflow(contentTypeUid, {\n populate: 'stageRequiredToPublish',\n });\n\n acc[contentTypeUid] = {\n ...contentTypeModel,\n hasReviewWorkflow: !!workflow,\n stageRequiredToPublish: workflow?.stageRequiredToPublish,\n };\n\n return acc;\n },\n {}\n );\n\n return contentTypeModelsMap;\n },\n\n async countActions(\n query: Modules.EntityService.Params.Pick<typeof RELEASE_ACTION_MODEL_UID, 'filters'>\n ) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_ACTION_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);\n },\n\n async update(\n actionId: UpdateReleaseAction.Request['params']['actionId'],\n releaseId: UpdateReleaseAction.Request['params']['releaseId'],\n update: UpdateReleaseAction.Request['body']\n ) {\n const action = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findOne({\n where: {\n id: actionId,\n release: {\n id: releaseId,\n releasedAt: {\n $null: true,\n },\n },\n },\n });\n\n if (!action) {\n throw new errors.NotFoundError(\n `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`\n );\n }\n\n const actionStatus =\n update.type === 'publish'\n ? await getDraftEntryValidStatus(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n },\n {\n strapi,\n }\n )\n : true;\n\n const updatedAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({\n where: {\n id: actionId,\n release: {\n id: releaseId,\n releasedAt: {\n $null: true,\n },\n },\n },\n data: {\n ...update,\n isEntryValid: actionStatus,\n },\n });\n\n getService('release', { strapi }).updateReleaseStatus(releaseId);\n\n return updatedAction;\n },\n\n async delete(\n actionId: DeleteReleaseAction.Request['params']['actionId'],\n releaseId: DeleteReleaseAction.Request['params']['releaseId']\n ) {\n const deletedAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).delete({\n where: {\n id: actionId,\n release: {\n id: releaseId,\n releasedAt: {\n $null: true,\n },\n },\n },\n });\n\n if (!deletedAction) {\n throw new errors.NotFoundError(\n `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`\n );\n }\n\n getService('release', { strapi }).updateReleaseStatus(releaseId);\n\n return deletedAction;\n },\n\n async validateActionsByContentTypes(contentTypeUids: UID.ContentType[]) {\n const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n contentType: {\n $in: contentTypeUids,\n },\n // We only want to validate actions that are going to be published\n type: 'publish',\n release: {\n releasedAt: {\n $null: true,\n },\n },\n },\n populate: { release: true },\n });\n\n const releasesUpdated: Data.ID[] = [];\n\n await async.map(actions, async (action: ReleaseAction) => {\n const isValid = await getDraftEntryValidStatus(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n },\n { strapi }\n );\n\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({\n where: {\n id: action.id,\n },\n data: {\n isEntryValid: isValid,\n },\n });\n\n if (!releasesUpdated.includes(action.release.id)) {\n releasesUpdated.push(action.release.id);\n }\n\n return {\n id: action.id,\n isEntryValid: isValid,\n };\n });\n\n if (releasesUpdated.length > 0) {\n await async.map(releasesUpdated, async (releaseId: number) => {\n await getService('release', { strapi }).updateReleaseStatus(releaseId);\n });\n }\n },\n };\n};\n\nexport type ReleaseActionService = ReturnType<typeof createReleaseActionService>;\n\nexport default createReleaseActionService;\n"],"names":["getGroupName","queryValue","_","getOr","createReleaseActionService","strapi","getLocalesDataForActions","plugin","allLocales","service","find","reduce","acc","locale","code","name","getContentTypesDataForActions","contentTypesUids","contentManagerContentTypeService","contentTypesData","contentTypeUid","contentTypeConfig","findConfiguration","uid","mainField","settings","displayName","getModel","info","create","releaseId","action","disableUpdateReleaseStatus","validateEntryData","validateUniqueEntry","getService","Promise","all","contentType","entryDocumentId","model","kind","document","db","query","findOne","select","errors","NotFoundError","documentId","release","RELEASE_MODEL_UID","where","id","releasedAt","ValidationError","actionStatus","type","getDraftEntryValidStatus","releaseAction","RELEASE_ACTION_MODEL_UID","data","isEntryValid","populate","updateReleaseStatus","findPage","dbQuery","get","transform","results","actions","pagination","populateBuilderService","actionsWithEntry","async","map","populateDeep","Infinity","build","entry","getEntry","status","getEntryStatus","groupActions","groupBy","contentTypeUids","includes","push","allReleaseContentTypesDictionary","allLocalesDictionary","formattedData","mainFieldValue","groupName","getContentTypeModelsFromActions","workflowsService","contentTypeModelsMap","accPromise","contentTypeModel","workflow","getAssignedWorkflow","hasReviewWorkflow","stageRequiredToPublish","countActions","count","update","actionId","$null","updatedAction","delete","deletedAction","validateActionsByContentTypes","findMany","$in","releasesUpdated","isValid","length"],"mappings":"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"}
1
+ {"version":3,"file":"release-action.mjs","sources":["../../../server/src/services/release-action.ts"],"sourcesContent":["import { errors, async } from '@strapi/utils';\n\nimport type { Core, Internal, Modules, UID, Data } from '@strapi/types';\n\nimport _ from 'lodash/fp';\n\nimport { AUDITED_EVENTS, RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';\nimport { emitAuditOnCommit } from '../audit-logs';\n\nimport type {\n CreateReleaseAction,\n GetReleaseActions,\n ReleaseAction,\n ReleaseActionGroupBy,\n UpdateReleaseAction,\n DeleteReleaseAction,\n} from '../../../shared/contracts/release-actions';\nimport type { Entity } from '../../../shared/types';\nimport { getService, getDraftEntryValidStatus, getEntry, getEntryStatus } from '../utils';\n\nconst getGroupName = (queryValue: string) => {\n switch (queryValue) {\n case 'contentType':\n return 'contentType.displayName';\n case 'type':\n return 'type';\n case 'locale':\n return _.getOr('No locale', 'locale.name');\n default:\n return 'contentType.displayName';\n }\n};\n\nexport interface Locale extends Entity {\n name: string;\n code: string;\n}\n\ntype LocaleDictionary = {\n [key: Locale['code']]: Pick<Locale, 'name' | 'code'>;\n};\n\nconst createReleaseActionService = ({ strapi }: { strapi: Core.Strapi }) => {\n const getLocalesDataForActions = async () => {\n if (!strapi.plugin('i18n')) {\n return {};\n }\n\n const allLocales: Locale[] = (await strapi.plugin('i18n').service('locales').find()) || [];\n return allLocales.reduce<LocaleDictionary>((acc, locale) => {\n acc[locale.code] = { name: locale.name, code: locale.code };\n\n return acc;\n }, {});\n };\n\n const getContentTypesDataForActions = async (\n contentTypesUids: ReleaseAction['contentType'][]\n ) => {\n const contentManagerContentTypeService = strapi\n .plugin('content-manager')\n .service('content-types');\n\n const contentTypesData: Record<\n Internal.UID.ContentType,\n { mainField: string; displayName: string }\n > = {};\n for (const contentTypeUid of contentTypesUids) {\n const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({\n uid: contentTypeUid,\n });\n\n contentTypesData[contentTypeUid] = {\n mainField: contentTypeConfig.settings.mainField,\n displayName: strapi.getModel(contentTypeUid).info.displayName,\n };\n }\n\n return contentTypesData;\n };\n\n return {\n async create(\n releaseId: CreateReleaseAction.Request['params']['releaseId'],\n action: CreateReleaseAction.Request['body'],\n { disableUpdateReleaseStatus = false }: { disableUpdateReleaseStatus?: boolean } = {}\n ) {\n const { validateEntryData, validateUniqueEntry } = getService('release-validation', {\n strapi,\n });\n\n await Promise.all([\n validateEntryData(action.contentType, action.entryDocumentId),\n validateUniqueEntry(releaseId, action),\n ]);\n\n // If we are adding a singleType, we need to append the documentId of that singleType\n const model = strapi.contentType(action.contentType);\n if (model.kind === 'singleType') {\n const document = await strapi.db.query(model.uid).findOne({ select: ['documentId'] });\n\n if (!document) {\n throw new errors.NotFoundError(`No entry found for contentType ${action.contentType}`);\n }\n\n action.entryDocumentId = document.documentId;\n }\n\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // If the action is a publish, check if the entry is valid\n // If the action is an unpublish, skip the validation\n const actionStatus =\n action.type === 'publish'\n ? await getDraftEntryValidStatus(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n },\n {\n strapi,\n }\n )\n : true;\n\n const releaseAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).create({\n data: {\n ...action,\n release: release.id,\n isEntryValid: actionStatus,\n },\n populate: { release: { select: ['id'] } },\n });\n\n if (!disableUpdateReleaseStatus) {\n getService('release', { strapi }).updateReleaseStatus(release.id);\n }\n\n await emitAuditOnCommit({ strapi }, AUDITED_EVENTS.RELEASE_ENTRY_ADD, {\n releaseId: release.id,\n name: release.name,\n actionId: releaseAction.id,\n type: releaseAction.type,\n contentType: releaseAction.contentType,\n entryDocumentId: releaseAction.entryDocumentId,\n locale: releaseAction.locale,\n });\n\n return releaseAction;\n },\n\n async findPage(\n releaseId: GetReleaseActions.Request['params']['releaseId'],\n query?: GetReleaseActions.Request['query']\n ) {\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n select: ['id'],\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const dbQuery = strapi.get('query-params').transform(RELEASE_ACTION_MODEL_UID, query ?? {});\n const { results: actions, pagination } = await strapi.db\n .query(RELEASE_ACTION_MODEL_UID)\n .findPage({\n ...dbQuery,\n where: {\n release: releaseId,\n },\n });\n\n // For each contentType on the release, we create a custom populate object for nested relations\n const populateBuilderService = strapi.plugin('content-manager').service('populate-builder');\n\n const actionsWithEntry = await async.map(actions, async (action: ReleaseAction) => {\n // @ts-expect-error - Core.Service type is not a function\n const populate = await populateBuilderService(action.contentType)\n .populateDeep(Infinity)\n .build();\n\n const entry = await getEntry(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n populate,\n status: action.type === 'publish' ? 'draft' : 'published',\n },\n { strapi }\n );\n\n return {\n ...action,\n entry,\n status: entry ? await getEntryStatus(action.contentType, entry) : null,\n };\n });\n\n return {\n results: actionsWithEntry,\n pagination,\n };\n },\n\n async groupActions(actions: ReleaseAction[], groupBy: ReleaseActionGroupBy) {\n const contentTypeUids = actions.reduce<ReleaseAction['contentType'][]>((acc, action) => {\n if (!acc.includes(action.contentType)) {\n acc.push(action.contentType);\n }\n\n return acc;\n }, []);\n const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);\n const allLocalesDictionary = await getLocalesDataForActions();\n\n const formattedData = actions.map((action: ReleaseAction) => {\n const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];\n\n return {\n ...action,\n locale: action.locale ? allLocalesDictionary[action.locale] : null,\n contentType: {\n displayName,\n mainFieldValue: action.entry[mainField],\n uid: action.contentType,\n },\n };\n });\n\n const groupName = getGroupName(groupBy);\n return _.groupBy(groupName)(formattedData);\n },\n\n async getContentTypeModelsFromActions(actions: ReleaseAction[]) {\n const contentTypeUids = actions.reduce<ReleaseAction['contentType'][]>((acc, action) => {\n if (!acc.includes(action.contentType)) {\n acc.push(action.contentType);\n }\n\n return acc;\n }, []);\n\n const workflowsService = strapi.plugin('review-workflows').service('workflows');\n\n const contentTypeModelsMap = await async.reduce(contentTypeUids)(\n async (\n accPromise: Promise<GetReleaseActions.Response['meta']['contentTypes']>,\n contentTypeUid: ReleaseAction['contentType']\n ) => {\n const acc = await accPromise;\n const contentTypeModel = strapi.getModel(contentTypeUid);\n\n // Workflows service may not be available depending on the license\n const workflow = await workflowsService?.getAssignedWorkflow(contentTypeUid, {\n populate: 'stageRequiredToPublish',\n });\n\n acc[contentTypeUid] = {\n ...contentTypeModel,\n hasReviewWorkflow: !!workflow,\n stageRequiredToPublish: workflow?.stageRequiredToPublish,\n };\n\n return acc;\n },\n {}\n );\n\n return contentTypeModelsMap;\n },\n\n async countActions(\n query: Modules.EntityService.Params.Pick<typeof RELEASE_ACTION_MODEL_UID, 'filters'>\n ) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_ACTION_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);\n },\n\n async update(\n actionId: UpdateReleaseAction.Request['params']['actionId'],\n releaseId: UpdateReleaseAction.Request['params']['releaseId'],\n update: UpdateReleaseAction.Request['body']\n ) {\n const action = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findOne({\n where: {\n id: actionId,\n release: {\n id: releaseId,\n releasedAt: {\n $null: true,\n },\n },\n },\n populate: { release: { select: ['id', 'name'] } },\n });\n\n if (!action) {\n throw new errors.NotFoundError(\n `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`\n );\n }\n\n const actionStatus =\n update.type === 'publish'\n ? await getDraftEntryValidStatus(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n },\n {\n strapi,\n }\n )\n : true;\n\n const updatedAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({\n where: {\n id: actionId,\n release: {\n id: releaseId,\n releasedAt: {\n $null: true,\n },\n },\n },\n // Only the field the PUT contract allows. Changing the entry is remove + add.\n data: {\n type: update.type,\n isEntryValid: actionStatus,\n },\n });\n\n getService('release', { strapi }).updateReleaseStatus(releaseId);\n\n // isEntryValid is recalculated, not edited: only the type change is audited\n if (updatedAction && action.type !== updatedAction.type) {\n await emitAuditOnCommit({ strapi }, AUDITED_EVENTS.RELEASE_ENTRY_UPDATE, {\n releaseId: action.release?.id ?? Number(releaseId),\n name: action.release?.name,\n actionId: updatedAction.id,\n contentType: updatedAction.contentType,\n entryDocumentId: updatedAction.entryDocumentId,\n locale: updatedAction.locale,\n from: action.type,\n to: updatedAction.type,\n });\n }\n\n return updatedAction;\n },\n\n async delete(\n actionId: DeleteReleaseAction.Request['params']['actionId'],\n releaseId: DeleteReleaseAction.Request['params']['releaseId']\n ) {\n const deletedRow = await strapi.db.query(RELEASE_ACTION_MODEL_UID).delete({\n where: {\n id: actionId,\n release: {\n id: releaseId,\n releasedAt: {\n $null: true,\n },\n },\n },\n populate: { release: { select: ['id', 'name'] } },\n });\n\n if (!deletedRow) {\n throw new errors.NotFoundError(\n `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`\n );\n }\n\n const { release: populatedRelease, ...deletedAction } = deletedRow;\n\n getService('release', { strapi }).updateReleaseStatus(releaseId);\n\n await emitAuditOnCommit({ strapi }, AUDITED_EVENTS.RELEASE_ENTRY_REMOVE, {\n releaseId: populatedRelease?.id ?? Number(releaseId),\n name: populatedRelease?.name,\n actionId: deletedAction.id,\n type: deletedAction.type,\n contentType: deletedAction.contentType,\n entryDocumentId: deletedAction.entryDocumentId,\n locale: deletedAction.locale,\n });\n\n return deletedAction;\n },\n\n async validateActionsByContentTypes(contentTypeUids: UID.ContentType[]) {\n const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n contentType: {\n $in: contentTypeUids,\n },\n // We only want to validate actions that are going to be published\n type: 'publish',\n release: {\n releasedAt: {\n $null: true,\n },\n },\n },\n populate: { release: true },\n });\n\n const releasesUpdated: Data.ID[] = [];\n\n await async.map(actions, async (action: ReleaseAction) => {\n const isValid = await getDraftEntryValidStatus(\n {\n contentType: action.contentType,\n documentId: action.entryDocumentId,\n locale: action.locale,\n },\n { strapi }\n );\n\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({\n where: {\n id: action.id,\n },\n data: {\n isEntryValid: isValid,\n },\n });\n\n if (!releasesUpdated.includes(action.release.id)) {\n releasesUpdated.push(action.release.id);\n }\n\n return {\n id: action.id,\n isEntryValid: isValid,\n };\n });\n\n if (releasesUpdated.length > 0) {\n await async.map(releasesUpdated, async (releaseId: number) => {\n await getService('release', { strapi }).updateReleaseStatus(releaseId);\n });\n }\n },\n };\n};\n\nexport type ReleaseActionService = ReturnType<typeof createReleaseActionService>;\n\nexport default createReleaseActionService;\n"],"names":["getGroupName","queryValue","_","getOr","createReleaseActionService","strapi","getLocalesDataForActions","plugin","allLocales","service","find","reduce","acc","locale","code","name","getContentTypesDataForActions","contentTypesUids","contentManagerContentTypeService","contentTypesData","contentTypeUid","contentTypeConfig","findConfiguration","uid","mainField","settings","displayName","getModel","info","create","releaseId","action","disableUpdateReleaseStatus","validateEntryData","validateUniqueEntry","getService","Promise","all","contentType","entryDocumentId","model","kind","document","db","query","findOne","select","errors","NotFoundError","documentId","release","RELEASE_MODEL_UID","where","id","releasedAt","ValidationError","actionStatus","type","getDraftEntryValidStatus","releaseAction","RELEASE_ACTION_MODEL_UID","data","isEntryValid","populate","updateReleaseStatus","emitAuditOnCommit","AUDITED_EVENTS","RELEASE_ENTRY_ADD","actionId","findPage","dbQuery","get","transform","results","actions","pagination","populateBuilderService","actionsWithEntry","async","map","populateDeep","Infinity","build","entry","getEntry","status","getEntryStatus","groupActions","groupBy","contentTypeUids","includes","push","allReleaseContentTypesDictionary","allLocalesDictionary","formattedData","mainFieldValue","groupName","getContentTypeModelsFromActions","workflowsService","contentTypeModelsMap","accPromise","contentTypeModel","workflow","getAssignedWorkflow","hasReviewWorkflow","stageRequiredToPublish","countActions","count","update","$null","updatedAction","RELEASE_ENTRY_UPDATE","Number","from","to","delete","deletedRow","populatedRelease","deletedAction","RELEASE_ENTRY_REMOVE","validateActionsByContentTypes","findMany","$in","releasesUpdated","isValid","length"],"mappings":";;;;;;AAoBA,MAAMA,eAAe,CAACC,UAAAA,GAAAA;IACpB,OAAQA,UAAAA;QACN,KAAK,aAAA;YACH,OAAO,yBAAA;QACT,KAAK,MAAA;YACH,OAAO,MAAA;QACT,KAAK,QAAA;YACH,OAAOC,CAAAA,CAAEC,KAAK,CAAC,WAAA,EAAa,aAAA,CAAA;AAC9B,QAAA;YACE,OAAO,yBAAA;AACX;AACF,CAAA;AAWA,MAAMC,0BAAAA,GAA6B,CAAC,EAAEC,MAAM,EAA2B,GAAA;AACrE,IAAA,MAAMC,wBAAAA,GAA2B,UAAA;AAC/B,QAAA,IAAI,CAACD,MAAAA,CAAOE,MAAM,CAAC,MAAA,CAAA,EAAS;AAC1B,YAAA,OAAO,EAAC;AACV,QAAA;AAEA,QAAA,MAAMC,UAAAA,GAAwB,MAAMH,MAAAA,CAAOE,MAAM,CAAC,MAAA,CAAA,CAAQE,OAAO,CAAC,SAAA,CAAA,CAAWC,IAAI,EAAA,IAAO,EAAE;AAC1F,QAAA,OAAOF,UAAAA,CAAWG,MAAM,CAAmB,CAACC,GAAAA,EAAKC,MAAAA,GAAAA;AAC/CD,YAAAA,GAAG,CAACC,MAAAA,CAAOC,IAAI,CAAC,GAAG;AAAEC,gBAAAA,IAAAA,EAAMF,OAAOE,IAAI;AAAED,gBAAAA,IAAAA,EAAMD,OAAOC;AAAK,aAAA;YAE1D,OAAOF,GAAAA;AACT,QAAA,CAAA,EAAG,EAAC,CAAA;AACN,IAAA,CAAA;AAEA,IAAA,MAAMI,gCAAgC,OACpCC,gBAAAA,GAAAA;AAEA,QAAA,MAAMC,mCAAmCb,MAAAA,CACtCE,MAAM,CAAC,iBAAA,CAAA,CACPE,OAAO,CAAC,eAAA,CAAA;AAEX,QAAA,MAAMU,mBAGF,EAAC;QACL,KAAK,MAAMC,kBAAkBH,gBAAAA,CAAkB;AAC7C,YAAA,MAAMI,iBAAAA,GAAoB,MAAMH,gCAAAA,CAAiCI,iBAAiB,CAAC;gBACjFC,GAAAA,EAAKH;AACP,aAAA,CAAA;YAEAD,gBAAgB,CAACC,eAAe,GAAG;gBACjCI,SAAAA,EAAWH,iBAAAA,CAAkBI,QAAQ,CAACD,SAAS;AAC/CE,gBAAAA,WAAAA,EAAarB,OAAOsB,QAAQ,CAACP,cAAAA,CAAAA,CAAgBQ,IAAI,CAACF;AACpD,aAAA;AACF,QAAA;QAEA,OAAOP,gBAAAA;AACT,IAAA,CAAA;IAEA,OAAO;QACL,MAAMU,MAAAA,CAAAA,CACJC,SAA6D,EAC7DC,MAA2C,EAC3C,EAAEC,0BAAAA,GAA6B,KAAK,EAA4C,GAAG,EAAE,EAAA;AAErF,YAAA,MAAM,EAAEC,iBAAiB,EAAEC,mBAAmB,EAAE,GAAGC,WAAW,oBAAA,EAAsB;AAClF9B,gBAAAA;AACF,aAAA,CAAA;YAEA,MAAM+B,OAAAA,CAAQC,GAAG,CAAC;AAChBJ,gBAAAA,iBAAAA,CAAkBF,MAAAA,CAAOO,WAAW,EAAEP,MAAAA,CAAOQ,eAAe,CAAA;AAC5DL,gBAAAA,mBAAAA,CAAoBJ,SAAAA,EAAWC,MAAAA;AAChC,aAAA,CAAA;;AAGD,YAAA,MAAMS,KAAAA,GAAQnC,MAAAA,CAAOiC,WAAW,CAACP,OAAOO,WAAW,CAAA;YACnD,IAAIE,KAAAA,CAAMC,IAAI,KAAK,YAAA,EAAc;gBAC/B,MAAMC,QAAAA,GAAW,MAAMrC,MAAAA,CAAOsC,EAAE,CAACC,KAAK,CAACJ,KAAAA,CAAMjB,GAAG,CAAA,CAAEsB,OAAO,CAAC;oBAAEC,MAAAA,EAAQ;AAAC,wBAAA;AAAa;AAAC,iBAAA,CAAA;AAEnF,gBAAA,IAAI,CAACJ,QAAAA,EAAU;oBACb,MAAM,IAAIK,OAAOC,aAAa,CAAC,CAAC,+BAA+B,EAAEjB,MAAAA,CAAOO,WAAW,CAAA,CAAE,CAAA;AACvF,gBAAA;gBAEAP,MAAAA,CAAOQ,eAAe,GAAGG,QAAAA,CAASO,UAAU;AAC9C,YAAA;YAEA,MAAMC,OAAAA,GAAU,MAAM7C,MAAAA,CAAOsC,EAAE,CAC5BC,KAAK,CAACO,iBAAAA,CAAAA,CACNN,OAAO,CAAC;gBAAEO,KAAAA,EAAO;oBAAEC,EAAAA,EAAIvB;AAAU;AAAE,aAAA,CAAA;AAEtC,YAAA,IAAI,CAACoB,OAAAA,EAAS;AACZ,gBAAA,MAAM,IAAIH,MAAAA,CAAOC,aAAa,CAAC,CAAC,wBAAwB,EAAElB,SAAAA,CAAAA,CAAW,CAAA;AACvE,YAAA;YAEA,IAAIoB,OAAAA,CAAQI,UAAU,EAAE;gBACtB,MAAM,IAAIP,MAAAA,CAAOQ,eAAe,CAAC,2BAAA,CAAA;AACnC,YAAA;;;AAIA,YAAA,MAAMC,eACJzB,MAAAA,CAAO0B,IAAI,KAAK,SAAA,GACZ,MAAMC,wBAAAA,CACJ;AACEpB,gBAAAA,WAAAA,EAAaP,OAAOO,WAAW;AAC/BW,gBAAAA,UAAAA,EAAYlB,OAAOQ,eAAe;AAClC1B,gBAAAA,MAAAA,EAAQkB,OAAOlB;aACjB,EACA;AACER,gBAAAA;aACF,CAAA,GAEF,IAAA;YAEN,MAAMsD,aAAAA,GAAgB,MAAMtD,MAAAA,CAAOsC,EAAE,CAACC,KAAK,CAACgB,wBAAAA,CAAAA,CAA0B/B,MAAM,CAAC;gBAC3EgC,IAAAA,EAAM;AACJ,oBAAA,GAAG9B,MAAM;AACTmB,oBAAAA,OAAAA,EAASA,QAAQG,EAAE;oBACnBS,YAAAA,EAAcN;AAChB,iBAAA;gBACAO,QAAAA,EAAU;oBAAEb,OAAAA,EAAS;wBAAEJ,MAAAA,EAAQ;AAAC,4BAAA;AAAK;AAAC;AAAE;AAC1C,aAAA,CAAA;AAEA,YAAA,IAAI,CAACd,0BAAAA,EAA4B;AAC/BG,gBAAAA,UAAAA,CAAW,SAAA,EAAW;AAAE9B,oBAAAA;iBAAO,CAAA,CAAG2D,mBAAmB,CAACd,OAAAA,CAAQG,EAAE,CAAA;AAClE,YAAA;AAEA,YAAA,MAAMY,iBAAAA,CAAkB;AAAE5D,gBAAAA;aAAO,EAAG6D,cAAAA,CAAeC,iBAAiB,EAAE;AACpErC,gBAAAA,SAAAA,EAAWoB,QAAQG,EAAE;AACrBtC,gBAAAA,IAAAA,EAAMmC,QAAQnC,IAAI;AAClBqD,gBAAAA,QAAAA,EAAUT,cAAcN,EAAE;AAC1BI,gBAAAA,IAAAA,EAAME,cAAcF,IAAI;AACxBnB,gBAAAA,WAAAA,EAAaqB,cAAcrB,WAAW;AACtCC,gBAAAA,eAAAA,EAAiBoB,cAAcpB,eAAe;AAC9C1B,gBAAAA,MAAAA,EAAQ8C,cAAc9C;AACxB,aAAA,CAAA;YAEA,OAAO8C,aAAAA;AACT,QAAA,CAAA;QAEA,MAAMU,QAAAA,CAAAA,CACJvC,SAA2D,EAC3Dc,KAA0C,EAAA;YAE1C,MAAMM,OAAAA,GAAU,MAAM7C,MAAAA,CAAOsC,EAAE,CAACC,KAAK,CAACO,iBAAAA,CAAAA,CAAmBN,OAAO,CAAC;gBAC/DO,KAAAA,EAAO;oBAAEC,EAAAA,EAAIvB;AAAU,iBAAA;gBACvBgB,MAAAA,EAAQ;AAAC,oBAAA;AAAK;AAChB,aAAA,CAAA;AAEA,YAAA,IAAI,CAACI,OAAAA,EAAS;AACZ,gBAAA,MAAM,IAAIH,MAAAA,CAAOC,aAAa,CAAC,CAAC,wBAAwB,EAAElB,SAAAA,CAAAA,CAAW,CAAA;AACvE,YAAA;YAEA,MAAMwC,OAAAA,GAAUjE,OAAOkE,GAAG,CAAC,gBAAgBC,SAAS,CAACZ,wBAAAA,EAA0BhB,KAAAA,IAAS,EAAC,CAAA;AACzF,YAAA,MAAM,EAAE6B,OAAAA,EAASC,OAAO,EAAEC,UAAU,EAAE,GAAG,MAAMtE,MAAAA,CAAOsC,EAAE,CACrDC,KAAK,CAACgB,wBAAAA,CAAAA,CACNS,QAAQ,CAAC;AACR,gBAAA,GAAGC,OAAO;gBACVlB,KAAAA,EAAO;oBACLF,OAAAA,EAASpB;AACX;AACF,aAAA,CAAA;;AAGF,YAAA,MAAM8C,yBAAyBvE,MAAAA,CAAOE,MAAM,CAAC,iBAAA,CAAA,CAAmBE,OAAO,CAAC,kBAAA,CAAA;AAExE,YAAA,MAAMoE,mBAAmB,MAAMC,KAAAA,CAAMC,GAAG,CAACL,SAAS,OAAO3C,MAAAA,GAAAA;;gBAEvD,MAAMgC,QAAAA,GAAW,MAAMa,sBAAAA,CAAuB7C,MAAAA,CAAOO,WAAW,CAAA,CAC7D0C,YAAY,CAACC,QAAAA,CAAAA,CACbC,KAAK,EAAA;gBAER,MAAMC,KAAAA,GAAQ,MAAMC,QAAAA,CAClB;AACE9C,oBAAAA,WAAAA,EAAaP,OAAOO,WAAW;AAC/BW,oBAAAA,UAAAA,EAAYlB,OAAOQ,eAAe;AAClC1B,oBAAAA,MAAAA,EAAQkB,OAAOlB,MAAM;AACrBkD,oBAAAA,QAAAA;AACAsB,oBAAAA,MAAAA,EAAQtD,MAAAA,CAAO0B,IAAI,KAAK,SAAA,GAAY,OAAA,GAAU;iBAChD,EACA;AAAEpD,oBAAAA;AAAO,iBAAA,CAAA;gBAGX,OAAO;AACL,oBAAA,GAAG0B,MAAM;AACToD,oBAAAA,KAAAA;AACAE,oBAAAA,MAAAA,EAAQF,QAAQ,MAAMG,cAAAA,CAAevD,MAAAA,CAAOO,WAAW,EAAE6C,KAAAA,CAAAA,GAAS;AACpE,iBAAA;AACF,YAAA,CAAA,CAAA;YAEA,OAAO;gBACLV,OAAAA,EAASI,gBAAAA;AACTF,gBAAAA;AACF,aAAA;AACF,QAAA,CAAA;QAEA,MAAMY,YAAAA,CAAAA,CAAab,OAAwB,EAAEc,OAA6B,EAAA;AACxE,YAAA,MAAMC,eAAAA,GAAkBf,OAAAA,CAAQ/D,MAAM,CAAiC,CAACC,GAAAA,EAAKmB,MAAAA,GAAAA;AAC3E,gBAAA,IAAI,CAACnB,GAAAA,CAAI8E,QAAQ,CAAC3D,MAAAA,CAAOO,WAAW,CAAA,EAAG;oBACrC1B,GAAAA,CAAI+E,IAAI,CAAC5D,MAAAA,CAAOO,WAAW,CAAA;AAC7B,gBAAA;gBAEA,OAAO1B,GAAAA;AACT,YAAA,CAAA,EAAG,EAAE,CAAA;YACL,MAAMgF,gCAAAA,GAAmC,MAAM5E,6BAAAA,CAA8ByE,eAAAA,CAAAA;AAC7E,YAAA,MAAMI,uBAAuB,MAAMvF,wBAAAA,EAAAA;AAEnC,YAAA,MAAMwF,aAAAA,GAAgBpB,OAAAA,CAAQK,GAAG,CAAC,CAAChD,MAAAA,GAAAA;gBACjC,MAAM,EAAEP,SAAS,EAAEE,WAAW,EAAE,GAAGkE,gCAAgC,CAAC7D,MAAAA,CAAOO,WAAW,CAAC;gBAEvF,OAAO;AACL,oBAAA,GAAGP,MAAM;oBACTlB,MAAAA,EAAQkB,MAAAA,CAAOlB,MAAM,GAAGgF,oBAAoB,CAAC9D,MAAAA,CAAOlB,MAAM,CAAC,GAAG,IAAA;oBAC9DyB,WAAAA,EAAa;AACXZ,wBAAAA,WAAAA;wBACAqE,cAAAA,EAAgBhE,MAAAA,CAAOoD,KAAK,CAAC3D,SAAAA,CAAU;AACvCD,wBAAAA,GAAAA,EAAKQ,OAAOO;AACd;AACF,iBAAA;AACF,YAAA,CAAA,CAAA;AAEA,YAAA,MAAM0D,YAAYhG,YAAAA,CAAawF,OAAAA,CAAAA;YAC/B,OAAOtF,CAAAA,CAAEsF,OAAO,CAACQ,SAAAA,CAAAA,CAAWF,aAAAA,CAAAA;AAC9B,QAAA,CAAA;AAEA,QAAA,MAAMG,iCAAgCvB,OAAwB,EAAA;AAC5D,YAAA,MAAMe,eAAAA,GAAkBf,OAAAA,CAAQ/D,MAAM,CAAiC,CAACC,GAAAA,EAAKmB,MAAAA,GAAAA;AAC3E,gBAAA,IAAI,CAACnB,GAAAA,CAAI8E,QAAQ,CAAC3D,MAAAA,CAAOO,WAAW,CAAA,EAAG;oBACrC1B,GAAAA,CAAI+E,IAAI,CAAC5D,MAAAA,CAAOO,WAAW,CAAA;AAC7B,gBAAA;gBAEA,OAAO1B,GAAAA;AACT,YAAA,CAAA,EAAG,EAAE,CAAA;AAEL,YAAA,MAAMsF,mBAAmB7F,MAAAA,CAAOE,MAAM,CAAC,kBAAA,CAAA,CAAoBE,OAAO,CAAC,WAAA,CAAA;AAEnE,YAAA,MAAM0F,uBAAuB,MAAMrB,KAAAA,CAAMnE,MAAM,CAAC8E,eAAAA,CAAAA,CAC9C,OACEW,UAAAA,EACAhF,cAAAA,GAAAA;AAEA,gBAAA,MAAMR,MAAM,MAAMwF,UAAAA;gBAClB,MAAMC,gBAAAA,GAAmBhG,MAAAA,CAAOsB,QAAQ,CAACP,cAAAA,CAAAA;;AAGzC,gBAAA,MAAMkF,QAAAA,GAAW,MAAMJ,gBAAAA,EAAkBK,mBAAAA,CAAoBnF,cAAAA,EAAgB;oBAC3E2C,QAAAA,EAAU;AACZ,iBAAA,CAAA;gBAEAnD,GAAG,CAACQ,eAAe,GAAG;AACpB,oBAAA,GAAGiF,gBAAgB;AACnBG,oBAAAA,iBAAAA,EAAmB,CAAC,CAACF,QAAAA;AACrBG,oBAAAA,sBAAAA,EAAwBH,QAAAA,EAAUG;AACpC,iBAAA;gBAEA,OAAO7F,GAAAA;AACT,YAAA,CAAA,EACA,EAAC,CAAA;YAGH,OAAOuF,oBAAAA;AACT,QAAA,CAAA;AAEA,QAAA,MAAMO,cACJ9D,KAAoF,EAAA;YAEpF,MAAM0B,OAAAA,GAAUjE,OAAOkE,GAAG,CAAC,gBAAgBC,SAAS,CAACZ,wBAAAA,EAA0BhB,KAAAA,IAAS,EAAC,CAAA;AAEzF,YAAA,OAAOvC,OAAOsC,EAAE,CAACC,KAAK,CAACgB,wBAAAA,CAAAA,CAA0B+C,KAAK,CAACrC,OAAAA,CAAAA;AACzD,QAAA,CAAA;AAEA,QAAA,MAAMsC,MAAAA,CAAAA,CACJxC,QAA2D,EAC3DtC,SAA6D,EAC7D8E,MAA2C,EAAA;YAE3C,MAAM7E,MAAAA,GAAS,MAAM1B,MAAAA,CAAOsC,EAAE,CAACC,KAAK,CAACgB,wBAAAA,CAAAA,CAA0Bf,OAAO,CAAC;gBACrEO,KAAAA,EAAO;oBACLC,EAAAA,EAAIe,QAAAA;oBACJlB,OAAAA,EAAS;wBACPG,EAAAA,EAAIvB,SAAAA;wBACJwB,UAAAA,EAAY;4BACVuD,KAAAA,EAAO;AACT;AACF;AACF,iBAAA;gBACA9C,QAAAA,EAAU;oBAAEb,OAAAA,EAAS;wBAAEJ,MAAAA,EAAQ;AAAC,4BAAA,IAAA;AAAM,4BAAA;AAAO;AAAC;AAAE;AAClD,aAAA,CAAA;AAEA,YAAA,IAAI,CAACf,MAAAA,EAAQ;AACX,gBAAA,MAAM,IAAIgB,MAAAA,CAAOC,aAAa,CAC5B,CAAC,eAAe,EAAEoB,QAAAA,CAAS,8BAA8B,EAAEtC,SAAAA,CAAU,2BAA2B,CAAC,CAAA;AAErG,YAAA;AAEA,YAAA,MAAM0B,eACJoD,MAAAA,CAAOnD,IAAI,KAAK,SAAA,GACZ,MAAMC,wBAAAA,CACJ;AACEpB,gBAAAA,WAAAA,EAAaP,OAAOO,WAAW;AAC/BW,gBAAAA,UAAAA,EAAYlB,OAAOQ,eAAe;AAClC1B,gBAAAA,MAAAA,EAAQkB,OAAOlB;aACjB,EACA;AACER,gBAAAA;aACF,CAAA,GAEF,IAAA;YAEN,MAAMyG,aAAAA,GAAgB,MAAMzG,MAAAA,CAAOsC,EAAE,CAACC,KAAK,CAACgB,wBAAAA,CAAAA,CAA0BgD,MAAM,CAAC;gBAC3ExD,KAAAA,EAAO;oBACLC,EAAAA,EAAIe,QAAAA;oBACJlB,OAAAA,EAAS;wBACPG,EAAAA,EAAIvB,SAAAA;wBACJwB,UAAAA,EAAY;4BACVuD,KAAAA,EAAO;AACT;AACF;AACF,iBAAA;;gBAEAhD,IAAAA,EAAM;AACJJ,oBAAAA,IAAAA,EAAMmD,OAAOnD,IAAI;oBACjBK,YAAAA,EAAcN;AAChB;AACF,aAAA,CAAA;AAEArB,YAAAA,UAAAA,CAAW,SAAA,EAAW;AAAE9B,gBAAAA;AAAO,aAAA,CAAA,CAAG2D,mBAAmB,CAAClC,SAAAA,CAAAA;;AAGtD,YAAA,IAAIgF,iBAAiB/E,MAAAA,CAAO0B,IAAI,KAAKqD,aAAAA,CAAcrD,IAAI,EAAE;AACvD,gBAAA,MAAMQ,iBAAAA,CAAkB;AAAE5D,oBAAAA;iBAAO,EAAG6D,cAAAA,CAAe6C,oBAAoB,EAAE;AACvEjF,oBAAAA,SAAAA,EAAWC,MAAAA,CAAOmB,OAAO,EAAEG,EAAAA,IAAM2D,MAAAA,CAAOlF,SAAAA,CAAAA;oBACxCf,IAAAA,EAAMgB,MAAAA,CAAOmB,OAAO,EAAEnC,IAAAA;AACtBqD,oBAAAA,QAAAA,EAAU0C,cAAczD,EAAE;AAC1Bf,oBAAAA,WAAAA,EAAawE,cAAcxE,WAAW;AACtCC,oBAAAA,eAAAA,EAAiBuE,cAAcvE,eAAe;AAC9C1B,oBAAAA,MAAAA,EAAQiG,cAAcjG,MAAM;AAC5BoG,oBAAAA,IAAAA,EAAMlF,OAAO0B,IAAI;AACjByD,oBAAAA,EAAAA,EAAIJ,cAAcrD;AACpB,iBAAA,CAAA;AACF,YAAA;YAEA,OAAOqD,aAAAA;AACT,QAAA,CAAA;QAEA,MAAMK,MAAAA,CAAAA,CACJ/C,QAA2D,EAC3DtC,SAA6D,EAAA;YAE7D,MAAMsF,UAAAA,GAAa,MAAM/G,MAAAA,CAAOsC,EAAE,CAACC,KAAK,CAACgB,wBAAAA,CAAAA,CAA0BuD,MAAM,CAAC;gBACxE/D,KAAAA,EAAO;oBACLC,EAAAA,EAAIe,QAAAA;oBACJlB,OAAAA,EAAS;wBACPG,EAAAA,EAAIvB,SAAAA;wBACJwB,UAAAA,EAAY;4BACVuD,KAAAA,EAAO;AACT;AACF;AACF,iBAAA;gBACA9C,QAAAA,EAAU;oBAAEb,OAAAA,EAAS;wBAAEJ,MAAAA,EAAQ;AAAC,4BAAA,IAAA;AAAM,4BAAA;AAAO;AAAC;AAAE;AAClD,aAAA,CAAA;AAEA,YAAA,IAAI,CAACsE,UAAAA,EAAY;AACf,gBAAA,MAAM,IAAIrE,MAAAA,CAAOC,aAAa,CAC5B,CAAC,eAAe,EAAEoB,QAAAA,CAAS,8BAA8B,EAAEtC,SAAAA,CAAU,2BAA2B,CAAC,CAAA;AAErG,YAAA;AAEA,YAAA,MAAM,EAAEoB,OAAAA,EAASmE,gBAAgB,EAAE,GAAGC,eAAe,GAAGF,UAAAA;AAExDjF,YAAAA,UAAAA,CAAW,SAAA,EAAW;AAAE9B,gBAAAA;AAAO,aAAA,CAAA,CAAG2D,mBAAmB,CAAClC,SAAAA,CAAAA;AAEtD,YAAA,MAAMmC,iBAAAA,CAAkB;AAAE5D,gBAAAA;aAAO,EAAG6D,cAAAA,CAAeqD,oBAAoB,EAAE;gBACvEzF,SAAAA,EAAWuF,gBAAAA,EAAkBhE,MAAM2D,MAAAA,CAAOlF,SAAAA,CAAAA;AAC1Cf,gBAAAA,IAAAA,EAAMsG,gBAAAA,EAAkBtG,IAAAA;AACxBqD,gBAAAA,QAAAA,EAAUkD,cAAcjE,EAAE;AAC1BI,gBAAAA,IAAAA,EAAM6D,cAAc7D,IAAI;AACxBnB,gBAAAA,WAAAA,EAAagF,cAAchF,WAAW;AACtCC,gBAAAA,eAAAA,EAAiB+E,cAAc/E,eAAe;AAC9C1B,gBAAAA,MAAAA,EAAQyG,cAAczG;AACxB,aAAA,CAAA;YAEA,OAAOyG,aAAAA;AACT,QAAA,CAAA;AAEA,QAAA,MAAME,+BAA8B/B,eAAkC,EAAA;YACpE,MAAMf,OAAAA,GAAU,MAAMrE,MAAAA,CAAOsC,EAAE,CAACC,KAAK,CAACgB,wBAAAA,CAAAA,CAA0B6D,QAAQ,CAAC;gBACvErE,KAAAA,EAAO;oBACLd,WAAAA,EAAa;wBACXoF,GAAAA,EAAKjC;AACP,qBAAA;;oBAEAhC,IAAAA,EAAM,SAAA;oBACNP,OAAAA,EAAS;wBACPI,UAAAA,EAAY;4BACVuD,KAAAA,EAAO;AACT;AACF;AACF,iBAAA;gBACA9C,QAAAA,EAAU;oBAAEb,OAAAA,EAAS;AAAK;AAC5B,aAAA,CAAA;AAEA,YAAA,MAAMyE,kBAA6B,EAAE;AAErC,YAAA,MAAM7C,KAAAA,CAAMC,GAAG,CAACL,OAAAA,EAAS,OAAO3C,MAAAA,GAAAA;gBAC9B,MAAM6F,OAAAA,GAAU,MAAMlE,wBAAAA,CACpB;AACEpB,oBAAAA,WAAAA,EAAaP,OAAOO,WAAW;AAC/BW,oBAAAA,UAAAA,EAAYlB,OAAOQ,eAAe;AAClC1B,oBAAAA,MAAAA,EAAQkB,OAAOlB;iBACjB,EACA;AAAER,oBAAAA;AAAO,iBAAA,CAAA;AAGX,gBAAA,MAAMA,OAAOsC,EAAE,CAACC,KAAK,CAACgB,wBAAAA,CAAAA,CAA0BgD,MAAM,CAAC;oBACrDxD,KAAAA,EAAO;AACLC,wBAAAA,EAAAA,EAAItB,OAAOsB;AACb,qBAAA;oBACAQ,IAAAA,EAAM;wBACJC,YAAAA,EAAc8D;AAChB;AACF,iBAAA,CAAA;gBAEA,IAAI,CAACD,gBAAgBjC,QAAQ,CAAC3D,OAAOmB,OAAO,CAACG,EAAE,CAAA,EAAG;AAChDsE,oBAAAA,eAAAA,CAAgBhC,IAAI,CAAC5D,MAAAA,CAAOmB,OAAO,CAACG,EAAE,CAAA;AACxC,gBAAA;gBAEA,OAAO;AACLA,oBAAAA,EAAAA,EAAItB,OAAOsB,EAAE;oBACbS,YAAAA,EAAc8D;AAChB,iBAAA;AACF,YAAA,CAAA,CAAA;YAEA,IAAID,eAAAA,CAAgBE,MAAM,GAAG,CAAA,EAAG;AAC9B,gBAAA,MAAM/C,KAAAA,CAAMC,GAAG,CAAC4C,eAAAA,EAAiB,OAAO7F,SAAAA,GAAAA;AACtC,oBAAA,MAAMK,WAAW,SAAA,EAAW;AAAE9B,wBAAAA;AAAO,qBAAA,CAAA,CAAG2D,mBAAmB,CAAClC,SAAAA,CAAAA;AAC9D,gBAAA,CAAA,CAAA;AACF,YAAA;AACF,QAAA;AACF,KAAA;AACF;;;;"}
@@ -3,6 +3,7 @@
3
3
  var utils = require('@strapi/utils');
4
4
  var constants = require('../constants.js');
5
5
  var index = require('../utils/index.js');
6
+ var auditLogs = require('../audit-logs.js');
6
7
 
7
8
  const createReleaseService = ({ strapi })=>{
8
9
  const dispatchWebhook = (event, { isPublished, release, error })=>{
@@ -63,6 +64,18 @@ const createReleaseService = ({ strapi })=>{
63
64
  status: 'empty'
64
65
  }
65
66
  });
67
+ // Audited before scheduling: a scheduling failure shouldn't leave the write
68
+ // unrecorded.
69
+ await auditLogs.emitAudit({
70
+ strapi
71
+ }, constants.AUDITED_EVENTS.RELEASE_CREATE, {
72
+ releaseId: release.id,
73
+ name: release.name,
74
+ ...release.scheduledAt && {
75
+ scheduledAt: release.scheduledAt,
76
+ timezone: release.timezone
77
+ }
78
+ });
66
79
  if (releaseWithCreatorFields.scheduledAt) {
67
80
  const schedulingService = index.getService('scheduling', {
68
81
  strapi
@@ -128,6 +141,20 @@ const createReleaseService = ({ strapi })=>{
128
141
  },
129
142
  data: releaseWithCreatorFields
130
143
  });
144
+ // Audited before scheduling, against the pre-image read next to the write.
145
+ // A write that matched no row (deleted in between) has nothing to report.
146
+ if (updatedRelease) {
147
+ const changes = auditLogs.getReleaseChanges(release, updatedRelease);
148
+ if (Object.keys(changes).length > 0) {
149
+ await auditLogs.emitAudit({
150
+ strapi
151
+ }, constants.AUDITED_EVENTS.RELEASE_UPDATE, {
152
+ releaseId: updatedRelease.id,
153
+ name: updatedRelease.name,
154
+ changes
155
+ });
156
+ }
157
+ }
131
158
  const schedulingService = index.getService('scheduling', {
132
159
  strapi
133
160
  });
@@ -193,14 +220,20 @@ const createReleaseService = ({ strapi })=>{
193
220
  await schedulingService.cancel(release.id);
194
221
  }
195
222
  strapi.telemetry.send('didDeleteContentRelease');
223
+ await auditLogs.emitAudit({
224
+ strapi
225
+ }, constants.AUDITED_EVENTS.RELEASE_DELETE, {
226
+ releaseId: release.id,
227
+ name: release.name
228
+ });
196
229
  return release;
197
230
  },
198
231
  async publish (releaseId) {
199
- const { release, error } = await strapi.db.transaction(async ({ trx })=>{
232
+ const { release, error, lockedRelease } = await strapi.db.transaction(async ({ trx })=>{
200
233
  /**
201
- * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished
202
- * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed
203
- */ const lockedRelease = await strapi.db?.queryBuilder(constants.RELEASE_MODEL_UID).where({
234
+ * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished
235
+ * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed
236
+ */ const lockedRelease = await strapi.db?.queryBuilder(constants.RELEASE_MODEL_UID).where({
204
237
  id: releaseId
205
238
  }).select([
206
239
  'id',
@@ -257,7 +290,8 @@ const createReleaseService = ({ strapi })=>{
257
290
  strapi.telemetry.send('didPublishContentRelease');
258
291
  return {
259
292
  release,
260
- error: null
293
+ error: null,
294
+ lockedRelease
261
295
  };
262
296
  } catch (error) {
263
297
  dispatchWebhook(constants.ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
@@ -274,15 +308,81 @@ const createReleaseService = ({ strapi })=>{
274
308
  // We want to throw the error after the transaction is finished, so we return the error
275
309
  return {
276
310
  release: null,
277
- error
311
+ // A rejection can carry any value, even null: never mistake it for success
312
+ error: error ?? new Error('Release publish failed with an empty error'),
313
+ lockedRelease
278
314
  };
279
315
  }
280
316
  });
281
- // Now the first transaction is commited, we can safely throw the error if it exists
282
- if (error instanceof Error) {
283
- throw error;
317
+ // The 'failed' status is already committed, so this emit cannot be rolled back.
318
+ // Pre-flight rejections threw before the run and never reach it.
319
+ if (error !== null) {
320
+ await auditLogs.emitAudit({
321
+ strapi
322
+ }, constants.AUDITED_EVENTS.RELEASE_TRIGGER, {
323
+ releaseId: lockedRelease.id,
324
+ name: lockedRelease.name,
325
+ outcome: 'failure',
326
+ // The error's name and nothing else from it: driver errors can carry row
327
+ // contents in their properties
328
+ reason: error instanceof Error ? error.name : 'Error'
329
+ });
330
+ // Swallowing a non-Error throwable would report the publish as successful
331
+ if (error instanceof Error) {
332
+ throw error;
333
+ }
334
+ throw new Error('Release publish failed', {
335
+ cause: error
336
+ });
337
+ }
338
+ // Counted after the publish, while the actions still exist.
339
+ // A count failure doesn't fail the committed publish: the error is returned.
340
+ const releaseActionService = index.getService('release-action', {
341
+ strapi
342
+ });
343
+ let counts = null;
344
+ let countsError = null;
345
+ try {
346
+ const [published, unpublished] = await Promise.all([
347
+ releaseActionService.countActions({
348
+ filters: {
349
+ release: releaseId,
350
+ type: 'publish'
351
+ }
352
+ }),
353
+ releaseActionService.countActions({
354
+ filters: {
355
+ release: releaseId,
356
+ type: 'unpublish'
357
+ }
358
+ })
359
+ ]);
360
+ counts = {
361
+ published,
362
+ unpublished
363
+ };
364
+ } catch (err) {
365
+ countsError = err instanceof Error ? err : new Error(String(err), {
366
+ cause: err
367
+ });
368
+ strapi.log.error(`Failed to count the entries for the release.trigger entry of release ${releaseId}`, {
369
+ error: countsError
370
+ });
284
371
  }
285
- return release;
372
+ await auditLogs.emitAudit({
373
+ strapi
374
+ }, constants.AUDITED_EVENTS.RELEASE_TRIGGER, {
375
+ releaseId: lockedRelease.id,
376
+ name: lockedRelease.name,
377
+ outcome: 'success',
378
+ published: counts?.published,
379
+ unpublished: counts?.unpublished
380
+ });
381
+ return {
382
+ release,
383
+ counts,
384
+ countsError
385
+ };
286
386
  },
287
387
  async updateReleaseStatus (releaseId) {
288
388
  const releaseActionService = index.getService('release-action', {
@@ -1 +1 @@
1
- {"version":3,"file":"release.js","sources":["../../../server/src/services/release.ts"],"sourcesContent":["import { setCreatorFields, errors } from '@strapi/utils';\n\nimport type { Core, Struct, UID, Data } from '@strapi/types';\n\nimport { ALLOWED_WEBHOOK_EVENTS, RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';\nimport type {\n GetReleases,\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n} from '../../../shared/contracts/releases';\nimport type { ReleaseAction } from '../../../shared/contracts/release-actions';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService, getPublishOrderForContentTypes } from '../utils';\n\nconst createReleaseService = ({ strapi }: { strapi: Core.Strapi }) => {\n const dispatchWebhook = (\n event: string,\n { isPublished, release, error }: { isPublished: boolean; release?: any; error?: unknown }\n ) => {\n strapi.eventHub.emit(event, {\n isPublished,\n error,\n release,\n });\n };\n\n /**\n * Given a release id, it returns the actions formatted ready to be used to publish them.\n * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.\n */\n const getFormattedActions = async (releaseId: Release['id']) => {\n const actions = (await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n release: {\n id: releaseId,\n },\n },\n })) as ReleaseAction[];\n\n if (actions.length === 0) {\n throw new errors.ValidationError('No entries to publish');\n }\n\n /**\n * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.\n */\n const formattedActions: {\n [key: UID.ContentType]: {\n publish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n unpublish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n };\n } = {};\n\n for (const action of actions) {\n const contentTypeUid: UID.ContentType = action.contentType;\n\n if (!formattedActions[contentTypeUid]) {\n formattedActions[contentTypeUid] = {\n publish: [],\n unpublish: [],\n };\n }\n\n formattedActions[contentTypeUid][action.type].push({\n documentId: action.entryDocumentId,\n locale: action.locale,\n });\n }\n\n return formattedActions;\n };\n\n return {\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const {\n validatePendingReleasesLimit,\n validateUniqueNameForPendingRelease,\n validateScheduledAtIsLaterThanNow,\n } = getService('release-validation', { strapi });\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).create({\n data: {\n ...releaseWithCreatorFields,\n status: 'empty',\n },\n });\n\n if (releaseWithCreatorFields.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n strapi.telemetry.send('didCreateContentRelease');\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n ...dbQuery,\n where: { id },\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findPage({\n ...dbQuery,\n populate: {\n actions: {\n count: true,\n },\n },\n });\n },\n\n findMany(query?: any) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findMany({\n ...dbQuery,\n });\n },\n\n async update(\n id: Data.ID,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(\n releaseData\n );\n\n const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${id}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: { id },\n data: releaseWithCreatorFields,\n });\n\n const schedulingService = getService('scheduling', { strapi });\n\n if (releaseData.scheduledAt) {\n // set function always cancel the previous job if it exists, so we can call it directly\n await schedulingService.set(id, releaseData.scheduledAt);\n } else if (release.scheduledAt) {\n // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it\n schedulingService.cancel(id);\n }\n\n this.updateReleaseStatus(id);\n\n strapi.telemetry.send('didUpdateContentRelease');\n\n return updatedRelease;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi\n .plugin('content-manager')\n .service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (\n acc: { [key: Struct.ComponentSchema['uid']]: Struct.ComponentSchema },\n component: Struct.ComponentSchema\n ) => {\n acc[component.uid] = component;\n\n return acc;\n },\n {}\n );\n\n return componentsMap;\n },\n\n async delete(releaseId: DeleteRelease.Request['params']['id']) {\n const release: Release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n populate: {\n actions: {\n select: ['id'],\n },\n },\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // Only delete the release and its actions is you in fact can delete all the actions and the release\n // Otherwise, if the transaction fails it throws an error\n await strapi.db.transaction(async () => {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n id: {\n $in: release.actions.map((action) => action.id),\n },\n },\n });\n\n await strapi.db.query(RELEASE_MODEL_UID).delete({\n where: {\n id: releaseId,\n },\n });\n });\n\n if (release.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n await schedulingService.cancel(release.id);\n }\n\n strapi.telemetry.send('didDeleteContentRelease');\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n const {\n release,\n error,\n }: { release: Pick<Release, 'id' | 'releasedAt' | 'status'> | null; error: unknown | null } =\n await strapi.db.transaction(async ({ trx }) => {\n /**\n * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished\n * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed\n */\n const lockedRelease = (await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .select(['id', 'name', 'releasedAt', 'status'])\n .first()\n .transacting(trx)\n .forUpdate()\n .execute()) as Pick<Release, 'id' | 'name' | 'releasedAt' | 'status'> | undefined;\n\n if (!lockedRelease) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (lockedRelease.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (lockedRelease.status === 'failed') {\n throw new errors.ValidationError('Release failed to publish');\n }\n\n try {\n strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);\n\n const formattedActions = await getFormattedActions(releaseId);\n\n // Publish content types in dependency order so that when entity A has a relation\n // to entity B, B is published first to keep this relation.\n const contentTypeUids = getPublishOrderForContentTypes(\n Object.keys(formattedActions) as UID.ContentType[],\n { strapi }\n );\n\n await strapi.db.transaction(async () => {\n for (const contentTypeUid of contentTypeUids) {\n const contentType = contentTypeUid as UID.ContentType;\n const { publish, unpublish } = formattedActions[contentType];\n\n // Serialize within a content type: concurrent publishes of related documents\n // can race on shared join-table state (notably self-referential relations) and\n // leave inconsistent FK rows when one branch deletes a row another branch is\n // about to reference.\n for (const params of publish) {\n await strapi.documents(contentType).publish(params);\n }\n\n for (const params of unpublish) {\n await strapi.documents(contentType).unpublish(params);\n }\n }\n });\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'done',\n releasedAt: new Date(),\n },\n });\n\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: true,\n release,\n });\n\n strapi.telemetry.send('didPublishContentRelease');\n\n return { release, error: null };\n } catch (error) {\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: false,\n error,\n });\n\n // We need to run the update in the same transaction because the release is locked\n await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .update({\n status: 'failed',\n })\n .transacting(trx)\n .execute();\n\n // At this point, we don't want to throw the error because if that happen we rollback the change in the release status\n // We want to throw the error after the transaction is finished, so we return the error\n return {\n release: null,\n error,\n };\n }\n });\n\n // Now the first transaction is commited, we can safely throw the error if it exists\n if (error instanceof Error) {\n throw error;\n }\n\n return release;\n },\n\n async updateReleaseStatus(releaseId: Release['id']) {\n const releaseActionService = getService('release-action', { strapi });\n\n const [totalActions, invalidActions] = await Promise.all([\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n },\n }),\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n isEntryValid: false,\n },\n }),\n ]);\n\n if (totalActions > 0) {\n if (invalidActions > 0) {\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'blocked',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'ready',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'empty',\n },\n });\n },\n };\n};\n\nexport type ReleaseService = ReturnType<typeof createReleaseService>;\n\nexport default createReleaseService;\n"],"names":["createReleaseService","strapi","dispatchWebhook","event","isPublished","release","error","eventHub","emit","getFormattedActions","releaseId","actions","db","query","RELEASE_ACTION_MODEL_UID","findMany","where","id","length","errors","ValidationError","formattedActions","action","contentTypeUid","contentType","publish","unpublish","type","push","documentId","entryDocumentId","locale","create","releaseData","user","releaseWithCreatorFields","setCreatorFields","validatePendingReleasesLimit","validateUniqueNameForPendingRelease","validateScheduledAtIsLaterThanNow","getService","Promise","all","name","scheduledAt","RELEASE_MODEL_UID","data","status","schedulingService","set","telemetry","send","findOne","dbQuery","get","transform","findPage","populate","count","update","isEdition","NotFoundError","releasedAt","updatedRelease","cancel","updateReleaseStatus","getAllComponents","contentManagerComponentsService","plugin","service","components","findAllComponents","componentsMap","reduce","acc","component","uid","delete","select","transaction","deleteMany","$in","map","trx","lockedRelease","queryBuilder","first","transacting","forUpdate","execute","log","info","contentTypeUids","getPublishOrderForContentTypes","Object","keys","params","documents","Date","ALLOWED_WEBHOOK_EVENTS","RELEASES_PUBLISH","Error","releaseActionService","totalActions","invalidActions","countActions","filters","isEntryValid"],"mappings":"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"}
1
+ {"version":3,"file":"release.js","sources":["../../../server/src/services/release.ts"],"sourcesContent":["import { setCreatorFields, errors } from '@strapi/utils';\n\nimport type { Core, Struct, UID, Data } from '@strapi/types';\n\nimport {\n ALLOWED_WEBHOOK_EVENTS,\n AUDITED_EVENTS,\n RELEASE_ACTION_MODEL_UID,\n RELEASE_MODEL_UID,\n} from '../constants';\nimport type {\n GetReleases,\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n} from '../../../shared/contracts/releases';\nimport type { ReleaseAction } from '../../../shared/contracts/release-actions';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService, getPublishOrderForContentTypes } from '../utils';\nimport { emitAudit, getReleaseChanges } from '../audit-logs';\n\nconst createReleaseService = ({ strapi }: { strapi: Core.Strapi }) => {\n const dispatchWebhook = (\n event: string,\n { isPublished, release, error }: { isPublished: boolean; release?: any; error?: unknown }\n ) => {\n strapi.eventHub.emit(event, {\n isPublished,\n error,\n release,\n });\n };\n\n /**\n * Given a release id, it returns the actions formatted ready to be used to publish them.\n * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.\n */\n const getFormattedActions = async (releaseId: Release['id']) => {\n const actions = (await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n release: {\n id: releaseId,\n },\n },\n })) as ReleaseAction[];\n\n if (actions.length === 0) {\n throw new errors.ValidationError('No entries to publish');\n }\n\n /**\n * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.\n */\n const formattedActions: {\n [key: UID.ContentType]: {\n publish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n unpublish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n };\n } = {};\n\n for (const action of actions) {\n const contentTypeUid: UID.ContentType = action.contentType;\n\n if (!formattedActions[contentTypeUid]) {\n formattedActions[contentTypeUid] = {\n publish: [],\n unpublish: [],\n };\n }\n\n formattedActions[contentTypeUid][action.type].push({\n documentId: action.entryDocumentId,\n locale: action.locale,\n });\n }\n\n return formattedActions;\n };\n\n return {\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const {\n validatePendingReleasesLimit,\n validateUniqueNameForPendingRelease,\n validateScheduledAtIsLaterThanNow,\n } = getService('release-validation', { strapi });\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).create({\n data: {\n ...releaseWithCreatorFields,\n status: 'empty',\n },\n });\n\n // Audited before scheduling: a scheduling failure shouldn't leave the write\n // unrecorded.\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_CREATE, {\n releaseId: release.id,\n name: release.name,\n ...(release.scheduledAt && {\n scheduledAt: release.scheduledAt,\n timezone: release.timezone,\n }),\n });\n\n if (releaseWithCreatorFields.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n strapi.telemetry.send('didCreateContentRelease');\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n ...dbQuery,\n where: { id },\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findPage({\n ...dbQuery,\n populate: {\n actions: {\n count: true,\n },\n },\n });\n },\n\n findMany(query?: any) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findMany({\n ...dbQuery,\n });\n },\n\n async update(\n id: Data.ID,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(\n releaseData\n );\n\n const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${id}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: { id },\n data: releaseWithCreatorFields,\n });\n\n // Audited before scheduling, against the pre-image read next to the write.\n // A write that matched no row (deleted in between) has nothing to report.\n if (updatedRelease) {\n const changes = getReleaseChanges(release, updatedRelease);\n\n if (Object.keys(changes).length > 0) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_UPDATE, {\n releaseId: updatedRelease.id,\n name: updatedRelease.name,\n changes,\n });\n }\n }\n\n const schedulingService = getService('scheduling', { strapi });\n\n if (releaseData.scheduledAt) {\n // set function always cancel the previous job if it exists, so we can call it directly\n await schedulingService.set(id, releaseData.scheduledAt);\n } else if (release.scheduledAt) {\n // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it\n schedulingService.cancel(id);\n }\n\n this.updateReleaseStatus(id);\n\n strapi.telemetry.send('didUpdateContentRelease');\n\n return updatedRelease;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi\n .plugin('content-manager')\n .service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (\n acc: { [key: Struct.ComponentSchema['uid']]: Struct.ComponentSchema },\n component: Struct.ComponentSchema\n ) => {\n acc[component.uid] = component;\n\n return acc;\n },\n {}\n );\n\n return componentsMap;\n },\n\n async delete(releaseId: DeleteRelease.Request['params']['id']) {\n const release: Release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n populate: {\n actions: {\n select: ['id'],\n },\n },\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // Only delete the release and its actions is you in fact can delete all the actions and the release\n // Otherwise, if the transaction fails it throws an error\n await strapi.db.transaction(async () => {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n id: {\n $in: release.actions.map((action) => action.id),\n },\n },\n });\n\n await strapi.db.query(RELEASE_MODEL_UID).delete({\n where: {\n id: releaseId,\n },\n });\n });\n\n if (release.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n await schedulingService.cancel(release.id);\n }\n\n strapi.telemetry.send('didDeleteContentRelease');\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_DELETE, {\n releaseId: release.id,\n name: release.name,\n });\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n const {\n release,\n error,\n lockedRelease,\n }: {\n release: Pick<Release, 'id' | 'releasedAt' | 'status'> | null;\n error: unknown;\n lockedRelease: Pick<Release, 'id' | 'name'>;\n } = await strapi.db.transaction(async ({ trx }) => {\n /**\n * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished\n * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed\n */\n const lockedRelease = (await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .select(['id', 'name', 'releasedAt', 'status'])\n .first()\n .transacting(trx)\n .forUpdate()\n .execute()) as Pick<Release, 'id' | 'name' | 'releasedAt' | 'status'> | undefined;\n\n if (!lockedRelease) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (lockedRelease.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (lockedRelease.status === 'failed') {\n throw new errors.ValidationError('Release failed to publish');\n }\n\n try {\n strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);\n\n const formattedActions = await getFormattedActions(releaseId);\n\n // Publish content types in dependency order so that when entity A has a relation\n // to entity B, B is published first to keep this relation.\n const contentTypeUids = getPublishOrderForContentTypes(\n Object.keys(formattedActions) as UID.ContentType[],\n { strapi }\n );\n\n await strapi.db.transaction(async () => {\n for (const contentTypeUid of contentTypeUids) {\n const contentType = contentTypeUid as UID.ContentType;\n const { publish, unpublish } = formattedActions[contentType];\n\n // Serialize within a content type: concurrent publishes of related documents\n // can race on shared join-table state (notably self-referential relations) and\n // leave inconsistent FK rows when one branch deletes a row another branch is\n // about to reference.\n for (const params of publish) {\n await strapi.documents(contentType).publish(params);\n }\n\n for (const params of unpublish) {\n await strapi.documents(contentType).unpublish(params);\n }\n }\n });\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'done',\n releasedAt: new Date(),\n },\n });\n\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: true,\n release,\n });\n\n strapi.telemetry.send('didPublishContentRelease');\n\n return { release, error: null, lockedRelease };\n } catch (error) {\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: false,\n error,\n });\n\n // We need to run the update in the same transaction because the release is locked\n await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .update({\n status: 'failed',\n })\n .transacting(trx)\n .execute();\n\n // At this point, we don't want to throw the error because if that happen we rollback the change in the release status\n // We want to throw the error after the transaction is finished, so we return the error\n return {\n release: null,\n // A rejection can carry any value, even null: never mistake it for success\n error: error ?? new Error('Release publish failed with an empty error'),\n lockedRelease,\n };\n }\n });\n\n // The 'failed' status is already committed, so this emit cannot be rolled back.\n // Pre-flight rejections threw before the run and never reach it.\n if (error !== null) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'failure',\n // The error's name and nothing else from it: driver errors can carry row\n // contents in their properties\n reason: error instanceof Error ? error.name : 'Error',\n });\n\n // Swallowing a non-Error throwable would report the publish as successful\n if (error instanceof Error) {\n throw error;\n }\n throw new Error('Release publish failed', { cause: error });\n }\n\n // Counted after the publish, while the actions still exist.\n // A count failure doesn't fail the committed publish: the error is returned.\n const releaseActionService = getService('release-action', { strapi });\n let counts = null;\n let countsError: Error | null = null;\n\n try {\n const [published, unpublished] = await Promise.all([\n releaseActionService.countActions({ filters: { release: releaseId, type: 'publish' } }),\n releaseActionService.countActions({\n filters: { release: releaseId, type: 'unpublish' },\n }),\n ]);\n counts = { published, unpublished };\n } catch (err) {\n countsError = err instanceof Error ? err : new Error(String(err), { cause: err });\n strapi.log.error(\n `Failed to count the entries for the release.trigger entry of release ${releaseId}`,\n { error: countsError }\n );\n }\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'success',\n published: counts?.published,\n unpublished: counts?.unpublished,\n });\n\n return { release, counts, countsError };\n },\n\n async updateReleaseStatus(releaseId: Release['id']) {\n const releaseActionService = getService('release-action', { strapi });\n\n const [totalActions, invalidActions] = await Promise.all([\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n },\n }),\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n isEntryValid: false,\n },\n }),\n ]);\n\n if (totalActions > 0) {\n if (invalidActions > 0) {\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'blocked',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'ready',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'empty',\n },\n });\n },\n };\n};\n\nexport type ReleaseService = ReturnType<typeof createReleaseService>;\n\nexport default createReleaseService;\n"],"names":["createReleaseService","strapi","dispatchWebhook","event","isPublished","release","error","eventHub","emit","getFormattedActions","releaseId","actions","db","query","RELEASE_ACTION_MODEL_UID","findMany","where","id","length","errors","ValidationError","formattedActions","action","contentTypeUid","contentType","publish","unpublish","type","push","documentId","entryDocumentId","locale","create","releaseData","user","releaseWithCreatorFields","setCreatorFields","validatePendingReleasesLimit","validateUniqueNameForPendingRelease","validateScheduledAtIsLaterThanNow","getService","Promise","all","name","scheduledAt","RELEASE_MODEL_UID","data","status","emitAudit","AUDITED_EVENTS","RELEASE_CREATE","timezone","schedulingService","set","telemetry","send","findOne","dbQuery","get","transform","findPage","populate","count","update","isEdition","NotFoundError","releasedAt","updatedRelease","changes","getReleaseChanges","Object","keys","RELEASE_UPDATE","cancel","updateReleaseStatus","getAllComponents","contentManagerComponentsService","plugin","service","components","findAllComponents","componentsMap","reduce","acc","component","uid","delete","select","transaction","deleteMany","$in","map","RELEASE_DELETE","lockedRelease","trx","queryBuilder","first","transacting","forUpdate","execute","log","info","contentTypeUids","getPublishOrderForContentTypes","params","documents","Date","ALLOWED_WEBHOOK_EVENTS","RELEASES_PUBLISH","Error","RELEASE_TRIGGER","outcome","reason","cause","releaseActionService","counts","countsError","published","unpublished","countActions","filters","err","String","totalActions","invalidActions","isEntryValid"],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { errors, setCreatorFields } from '@strapi/utils';
2
- import { RELEASE_MODEL_UID, ALLOWED_WEBHOOK_EVENTS, RELEASE_ACTION_MODEL_UID } from '../constants.mjs';
2
+ import { RELEASE_MODEL_UID, ALLOWED_WEBHOOK_EVENTS, AUDITED_EVENTS, RELEASE_ACTION_MODEL_UID } from '../constants.mjs';
3
3
  import { getService, getPublishOrderForContentTypes } from '../utils/index.mjs';
4
+ import { emitAudit, getReleaseChanges } from '../audit-logs.mjs';
4
5
 
5
6
  const createReleaseService = ({ strapi })=>{
6
7
  const dispatchWebhook = (event, { isPublished, release, error })=>{
@@ -61,6 +62,18 @@ const createReleaseService = ({ strapi })=>{
61
62
  status: 'empty'
62
63
  }
63
64
  });
65
+ // Audited before scheduling: a scheduling failure shouldn't leave the write
66
+ // unrecorded.
67
+ await emitAudit({
68
+ strapi
69
+ }, AUDITED_EVENTS.RELEASE_CREATE, {
70
+ releaseId: release.id,
71
+ name: release.name,
72
+ ...release.scheduledAt && {
73
+ scheduledAt: release.scheduledAt,
74
+ timezone: release.timezone
75
+ }
76
+ });
64
77
  if (releaseWithCreatorFields.scheduledAt) {
65
78
  const schedulingService = getService('scheduling', {
66
79
  strapi
@@ -126,6 +139,20 @@ const createReleaseService = ({ strapi })=>{
126
139
  },
127
140
  data: releaseWithCreatorFields
128
141
  });
142
+ // Audited before scheduling, against the pre-image read next to the write.
143
+ // A write that matched no row (deleted in between) has nothing to report.
144
+ if (updatedRelease) {
145
+ const changes = getReleaseChanges(release, updatedRelease);
146
+ if (Object.keys(changes).length > 0) {
147
+ await emitAudit({
148
+ strapi
149
+ }, AUDITED_EVENTS.RELEASE_UPDATE, {
150
+ releaseId: updatedRelease.id,
151
+ name: updatedRelease.name,
152
+ changes
153
+ });
154
+ }
155
+ }
129
156
  const schedulingService = getService('scheduling', {
130
157
  strapi
131
158
  });
@@ -191,14 +218,20 @@ const createReleaseService = ({ strapi })=>{
191
218
  await schedulingService.cancel(release.id);
192
219
  }
193
220
  strapi.telemetry.send('didDeleteContentRelease');
221
+ await emitAudit({
222
+ strapi
223
+ }, AUDITED_EVENTS.RELEASE_DELETE, {
224
+ releaseId: release.id,
225
+ name: release.name
226
+ });
194
227
  return release;
195
228
  },
196
229
  async publish (releaseId) {
197
- const { release, error } = await strapi.db.transaction(async ({ trx })=>{
230
+ const { release, error, lockedRelease } = await strapi.db.transaction(async ({ trx })=>{
198
231
  /**
199
- * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished
200
- * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed
201
- */ const lockedRelease = await strapi.db?.queryBuilder(RELEASE_MODEL_UID).where({
232
+ * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished
233
+ * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed
234
+ */ const lockedRelease = await strapi.db?.queryBuilder(RELEASE_MODEL_UID).where({
202
235
  id: releaseId
203
236
  }).select([
204
237
  'id',
@@ -255,7 +288,8 @@ const createReleaseService = ({ strapi })=>{
255
288
  strapi.telemetry.send('didPublishContentRelease');
256
289
  return {
257
290
  release,
258
- error: null
291
+ error: null,
292
+ lockedRelease
259
293
  };
260
294
  } catch (error) {
261
295
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
@@ -272,15 +306,81 @@ const createReleaseService = ({ strapi })=>{
272
306
  // We want to throw the error after the transaction is finished, so we return the error
273
307
  return {
274
308
  release: null,
275
- error
309
+ // A rejection can carry any value, even null: never mistake it for success
310
+ error: error ?? new Error('Release publish failed with an empty error'),
311
+ lockedRelease
276
312
  };
277
313
  }
278
314
  });
279
- // Now the first transaction is commited, we can safely throw the error if it exists
280
- if (error instanceof Error) {
281
- throw error;
315
+ // The 'failed' status is already committed, so this emit cannot be rolled back.
316
+ // Pre-flight rejections threw before the run and never reach it.
317
+ if (error !== null) {
318
+ await emitAudit({
319
+ strapi
320
+ }, AUDITED_EVENTS.RELEASE_TRIGGER, {
321
+ releaseId: lockedRelease.id,
322
+ name: lockedRelease.name,
323
+ outcome: 'failure',
324
+ // The error's name and nothing else from it: driver errors can carry row
325
+ // contents in their properties
326
+ reason: error instanceof Error ? error.name : 'Error'
327
+ });
328
+ // Swallowing a non-Error throwable would report the publish as successful
329
+ if (error instanceof Error) {
330
+ throw error;
331
+ }
332
+ throw new Error('Release publish failed', {
333
+ cause: error
334
+ });
335
+ }
336
+ // Counted after the publish, while the actions still exist.
337
+ // A count failure doesn't fail the committed publish: the error is returned.
338
+ const releaseActionService = getService('release-action', {
339
+ strapi
340
+ });
341
+ let counts = null;
342
+ let countsError = null;
343
+ try {
344
+ const [published, unpublished] = await Promise.all([
345
+ releaseActionService.countActions({
346
+ filters: {
347
+ release: releaseId,
348
+ type: 'publish'
349
+ }
350
+ }),
351
+ releaseActionService.countActions({
352
+ filters: {
353
+ release: releaseId,
354
+ type: 'unpublish'
355
+ }
356
+ })
357
+ ]);
358
+ counts = {
359
+ published,
360
+ unpublished
361
+ };
362
+ } catch (err) {
363
+ countsError = err instanceof Error ? err : new Error(String(err), {
364
+ cause: err
365
+ });
366
+ strapi.log.error(`Failed to count the entries for the release.trigger entry of release ${releaseId}`, {
367
+ error: countsError
368
+ });
282
369
  }
283
- return release;
370
+ await emitAudit({
371
+ strapi
372
+ }, AUDITED_EVENTS.RELEASE_TRIGGER, {
373
+ releaseId: lockedRelease.id,
374
+ name: lockedRelease.name,
375
+ outcome: 'success',
376
+ published: counts?.published,
377
+ unpublished: counts?.unpublished
378
+ });
379
+ return {
380
+ release,
381
+ counts,
382
+ countsError
383
+ };
284
384
  },
285
385
  async updateReleaseStatus (releaseId) {
286
386
  const releaseActionService = getService('release-action', {