@strapi/content-releases 4.20.0 → 4.20.1

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 (29) hide show
  1. package/dist/_chunks/{App-x6I6piPB.js → App-1hHIqUoZ.js} +110 -107
  2. package/dist/_chunks/App-1hHIqUoZ.js.map +1 -0
  3. package/dist/_chunks/{App-M11U5GPh.mjs → App-U6GbyLIE.mjs} +112 -109
  4. package/dist/_chunks/App-U6GbyLIE.mjs.map +1 -0
  5. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
  6. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
  7. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
  8. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
  9. package/dist/_chunks/{en-oREbrNEq.mjs → en-GqXgfmzl.mjs} +4 -1
  10. package/dist/_chunks/en-GqXgfmzl.mjs.map +1 -0
  11. package/dist/_chunks/{en--WvbU-_H.js → en-bDhIlw-B.js} +4 -1
  12. package/dist/_chunks/en-bDhIlw-B.js.map +1 -0
  13. package/dist/_chunks/{index-Ls9Qc5Tg.mjs → index-gkExFBa0.mjs} +55 -14
  14. package/dist/_chunks/index-gkExFBa0.mjs.map +1 -0
  15. package/dist/_chunks/{index-M4gICzzI.js → index-l-FvkQlQ.js} +55 -14
  16. package/dist/_chunks/index-l-FvkQlQ.js.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +1 -1
  19. package/dist/server/index.js +128 -13
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +128 -13
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +10 -9
  24. package/dist/_chunks/App-M11U5GPh.mjs.map +0 -1
  25. package/dist/_chunks/App-x6I6piPB.js.map +0 -1
  26. package/dist/_chunks/en--WvbU-_H.js.map +0 -1
  27. package/dist/_chunks/en-oREbrNEq.mjs.map +0 -1
  28. package/dist/_chunks/index-Ls9Qc5Tg.mjs.map +0 -1
  29. package/dist/_chunks/index-M4gICzzI.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../server/src/constants.ts","../../server/src/migrations/index.ts","../../server/src/register.ts","../../server/src/bootstrap.ts","../../server/src/content-types/release/schema.ts","../../server/src/content-types/release/index.ts","../../server/src/content-types/release-action/schema.ts","../../server/src/content-types/release-action/index.ts","../../server/src/content-types/index.ts","../../server/src/utils/index.ts","../../server/src/services/release.ts","../../server/src/services/validation.ts","../../server/src/services/index.ts","../../shared/validation-schemas.ts","../../server/src/controllers/validation/release.ts","../../server/src/controllers/release.ts","../../server/src/controllers/validation/release-action.ts","../../server/src/controllers/release-action.ts","../../server/src/controllers/index.ts","../../server/src/routes/release.ts","../../server/src/routes/release-action.ts","../../server/src/routes/index.ts","../../server/src/index.ts"],"sourcesContent":["export const RELEASE_MODEL_UID = 'plugin::content-releases.release';\nexport const RELEASE_ACTION_MODEL_UID = 'plugin::content-releases.release-action';\n\nexport const ACTIONS = [\n {\n section: 'plugins',\n displayName: 'Read',\n uid: 'read',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Create',\n uid: 'create',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Edit',\n uid: 'update',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Delete',\n uid: 'delete',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Publish',\n uid: 'publish',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Remove an entry from a release',\n uid: 'delete-action',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Add an entry to a release',\n uid: 'create-action',\n pluginName: 'content-releases',\n },\n];\n","import type { Schema } from '@strapi/types';\nimport { contentTypes as contentTypesUtils, mapAsync } from '@strapi/utils';\n\nimport { difference, keys } from 'lodash';\nimport { RELEASE_ACTION_MODEL_UID } from '../constants';\n\ninterface Input {\n oldContentTypes: Record<string, Schema.ContentType>;\n contentTypes: Record<string, Schema.ContentType>;\n}\n\nexport async function deleteActionsOnDisableDraftAndPublish({\n oldContentTypes,\n contentTypes,\n}: Input) {\n if (!oldContentTypes) {\n return;\n }\n\n for (const uid in contentTypes) {\n if (!oldContentTypes[uid]) {\n continue;\n }\n\n const oldContentType = oldContentTypes[uid];\n const contentType = contentTypes[uid];\n\n if (\n contentTypesUtils.hasDraftAndPublish(oldContentType) &&\n !contentTypesUtils.hasDraftAndPublish(contentType)\n ) {\n await strapi.db\n ?.queryBuilder(RELEASE_ACTION_MODEL_UID)\n .delete()\n .where({ contentType: uid })\n .execute();\n }\n }\n}\n\nexport async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes }: Input) {\n const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes)) ?? [];\n\n if (deletedContentTypes.length) {\n await mapAsync(deletedContentTypes, async (deletedContentTypeUID: unknown) => {\n return strapi.db\n ?.queryBuilder(RELEASE_ACTION_MODEL_UID)\n .delete()\n .where({ contentType: deletedContentTypeUID })\n .execute();\n });\n }\n}\n","/* eslint-disable @typescript-eslint/no-var-requires */\nimport type { LoadedStrapi } from '@strapi/types';\n\nimport { ACTIONS } from './constants';\nimport {\n deleteActionsOnDeleteContentType,\n deleteActionsOnDisableDraftAndPublish,\n} from './migrations';\n\nconst { features } = require('@strapi/strapi/dist/utils/ee');\n\nexport const register = async ({ strapi }: { strapi: LoadedStrapi }) => {\n if (features.isEnabled('cms-content-releases')) {\n await strapi.admin.services.permission.actionProvider.registerMany(ACTIONS);\n\n strapi.hook('strapi::content-types.beforeSync').register(deleteActionsOnDisableDraftAndPublish);\n strapi.hook('strapi::content-types.afterSync').register(deleteActionsOnDeleteContentType);\n }\n};\n","/* eslint-disable @typescript-eslint/no-var-requires */\nimport type { LoadedStrapi, Entity as StrapiEntity } from '@strapi/types';\n\nimport { RELEASE_ACTION_MODEL_UID } from './constants';\n\nconst { features } = require('@strapi/strapi/dist/utils/ee');\n\nexport const bootstrap = async ({ strapi }: { strapi: LoadedStrapi }) => {\n if (features.isEnabled('cms-content-releases')) {\n // Clean up release-actions when an entry is deleted\n strapi.db.lifecycles.subscribe({\n afterDelete(event) {\n // @ts-expect-error TODO: lifecycles types looks like are not 100% finished\n const { model, result } = event;\n // @ts-expect-error TODO: lifecycles types looks like are not 100% finished\n if (model.kind === 'collectionType' && model.options?.draftAndPublish) {\n const { id } = result;\n strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n target_type: model.uid,\n target_id: id,\n },\n });\n }\n },\n /**\n * deleteMany hook doesn't return the deleted entries ids\n * so we need to fetch them before deleting the entries to save the ids on our state\n */\n async beforeDeleteMany(event) {\n const { model, params } = event;\n // @ts-expect-error TODO: lifecycles types looks like are not 100% finished\n if (model.kind === 'collectionType' && model.options?.draftAndPublish) {\n const { where } = params;\n const entriesToDelete = await strapi.db\n .query(model.uid)\n .findMany({ select: ['id'], where });\n event.state.entriesToDelete = entriesToDelete;\n }\n },\n /**\n * We delete the release actions related to deleted entries\n * We make this only after deleteMany is succesfully executed to avoid errors\n */\n async afterDeleteMany(event) {\n const { model, state } = event;\n const entriesToDelete = state.entriesToDelete;\n if (entriesToDelete) {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n target_type: model.uid,\n target_id: {\n $in: (entriesToDelete as Array<{ id: StrapiEntity.ID }>).map((entry) => entry.id),\n },\n },\n });\n }\n },\n });\n }\n};\n","import { RELEASE_ACTION_MODEL_UID } from '../../constants';\n\nexport default {\n collectionName: 'strapi_releases',\n info: {\n singularName: 'release',\n pluralName: 'releases',\n displayName: 'Release',\n },\n options: {\n draftAndPublish: false,\n },\n pluginOptions: {\n 'content-manager': {\n visible: false,\n },\n 'content-type-builder': {\n visible: false,\n },\n },\n attributes: {\n name: {\n type: 'string',\n required: true,\n },\n releasedAt: {\n type: 'datetime',\n },\n actions: {\n type: 'relation',\n relation: 'oneToMany',\n target: RELEASE_ACTION_MODEL_UID,\n mappedBy: 'release',\n },\n },\n};\n","import schema from './schema';\n\nexport const release = {\n schema,\n};\n","import { RELEASE_MODEL_UID } from '../../constants';\n\nexport default {\n collectionName: 'strapi_release_actions',\n info: {\n singularName: 'release-action',\n pluralName: 'release-actions',\n displayName: 'Release Action',\n },\n options: {\n draftAndPublish: false,\n },\n pluginOptions: {\n 'content-manager': {\n visible: false,\n },\n 'content-type-builder': {\n visible: false,\n },\n },\n attributes: {\n type: {\n type: 'enumeration',\n enum: ['publish', 'unpublish'],\n required: true,\n },\n entry: {\n type: 'relation',\n relation: 'morphToOne',\n configurable: false,\n },\n contentType: {\n type: 'string',\n required: true,\n },\n locale: {\n type: 'string',\n },\n release: {\n type: 'relation',\n relation: 'manyToOne',\n target: RELEASE_MODEL_UID,\n inversedBy: 'actions',\n },\n },\n};\n","import schema from './schema';\n\nexport const releaseAction = {\n schema,\n};\n","import { release } from './release';\nimport { releaseAction } from './release-action';\n\nexport const contentTypes = {\n release,\n 'release-action': releaseAction,\n};\n","export const getService = (\n name: 'release' | 'release-validation' | 'release-action' | 'event-manager',\n { strapi } = { strapi: global.strapi }\n) => {\n return strapi.plugin('content-releases').service(name);\n};\n","import { setCreatorFields, errors } from '@strapi/utils';\n\nimport type { LoadedStrapi, EntityService, UID, Schema } from '@strapi/types';\n\nimport _ from 'lodash/fp';\n\nimport { 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 GetContentTypeEntryReleases,\n} from '../../../shared/contracts/releases';\nimport type {\n CreateReleaseAction,\n GetReleaseActions,\n ReleaseAction,\n UpdateReleaseAction,\n DeleteReleaseAction,\n ReleaseActionGroupBy,\n} from '../../../shared/contracts/release-actions';\nimport type { Entity, UserInfo } from '../../../shared/types';\nimport { getService } from '../utils';\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 getGroupName = (queryValue?: ReleaseActionGroupBy) => {\n switch (queryValue) {\n case 'contentType':\n return 'contentType.displayName';\n case 'action':\n return 'type';\n case 'locale':\n return _.getOr('No locale', 'locale.name');\n default:\n return 'contentType.displayName';\n }\n};\n\nconst createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n ]);\n\n return strapi.entityService.create(RELEASE_MODEL_UID, {\n data: releaseWithCreatorFields,\n });\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const release = await strapi.entityService.findOne(RELEASE_MODEL_UID, id, {\n ...query,\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n return strapi.entityService.findPage(RELEASE_MODEL_UID, {\n ...query,\n populate: {\n actions: {\n // @ts-expect-error Ignore missing properties\n count: true,\n },\n },\n });\n },\n\n async findManyWithContentTypeEntryAttached(\n contentTypeUid: GetContentTypeEntryReleases.Request['query']['contentTypeUid'],\n entryId: GetContentTypeEntryReleases.Request['query']['entryId']\n ) {\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n actions: {\n target_type: contentTypeUid,\n target_id: entryId,\n },\n releasedAt: {\n $null: true,\n },\n },\n populate: {\n // Filter the action to get only the content type entry\n actions: {\n where: {\n target_type: contentTypeUid,\n target_id: entryId,\n },\n },\n },\n });\n\n return releases.map((release) => {\n if (release.actions?.length) {\n const [actionForEntry] = release.actions;\n\n // Remove the actions key to replace it with an action key\n delete release.actions;\n\n return {\n ...release,\n action: actionForEntry,\n };\n }\n\n return release;\n });\n },\n\n async findManyWithoutContentTypeEntryAttached(\n contentTypeUid: GetContentTypeEntryReleases.Request['query']['contentTypeUid'],\n entryId: GetContentTypeEntryReleases.Request['query']['entryId']\n ) {\n // We get the list of releases where the entry is present\n const releasesRelated = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n releasedAt: {\n $null: true,\n },\n actions: {\n target_type: contentTypeUid,\n target_id: entryId,\n },\n },\n });\n\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n $or: [\n {\n id: {\n $notIn: releasesRelated.map((release) => release.id),\n },\n },\n {\n actions: null,\n },\n ],\n releasedAt: {\n $null: true,\n },\n },\n });\n\n return releases.map((release) => {\n if (release.actions?.length) {\n const [actionForEntry] = release.actions;\n\n // Remove the actions key to replace it with an action key\n delete release.actions;\n\n return {\n ...release,\n action: actionForEntry,\n };\n }\n\n return release;\n });\n },\n\n async update(\n id: number,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(releaseData);\n\n const release = await strapi.entityService.findOne(RELEASE_MODEL_UID, 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.entityService.update(RELEASE_MODEL_UID, id, {\n /*\n * The type returned from the entity service: Partial<Input<\"plugin::content-releases.release\">>\n * is not compatible with the type we are passing here: UpdateRelease.Request['body']\n */\n // @ts-expect-error see above\n data: releaseWithCreatorFields,\n });\n\n return updatedRelease;\n },\n\n async createAction(\n releaseId: CreateReleaseAction.Request['params']['releaseId'],\n action: Pick<CreateReleaseAction.Request['body'], 'type' | 'entry'>\n ) {\n const { validateEntryContentType, validateUniqueEntry } = getService('release-validation', {\n strapi,\n });\n\n await Promise.all([\n validateEntryContentType(action.entry.contentType),\n validateUniqueEntry(releaseId, action),\n ]);\n\n const release = await strapi.entityService.findOne(RELEASE_MODEL_UID, 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 const { entry, type } = action;\n\n return strapi.entityService.create(RELEASE_ACTION_MODEL_UID, {\n data: {\n type,\n contentType: entry.contentType,\n locale: entry.locale,\n entry: {\n id: entry.id,\n __type: entry.contentType,\n __pivot: { field: 'entry' },\n },\n release: releaseId,\n },\n populate: { release: { fields: ['id'] }, entry: { fields: ['id'] } },\n });\n },\n\n async findActions(\n releaseId: GetReleaseActions.Request['params']['releaseId'],\n query?: GetReleaseActions.Request['query']\n ) {\n const release = await strapi.entityService.findOne(RELEASE_MODEL_UID, releaseId, {\n fields: ['id'],\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n return strapi.entityService.findPage(RELEASE_ACTION_MODEL_UID, {\n ...query,\n populate: {\n entry: {\n populate: '*',\n },\n },\n filters: {\n release: releaseId,\n },\n });\n },\n\n async countActions(query: EntityService.Params.Pick<typeof RELEASE_ACTION_MODEL_UID, 'filters'>) {\n return strapi.entityService.count(RELEASE_ACTION_MODEL_UID, query);\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 this.getContentTypesDataForActions(\n contentTypeUids\n );\n const allLocalesDictionary = await this.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 getLocalesDataForActions() {\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 async getContentTypesDataForActions(contentTypesUids: ReleaseAction['contentType'][]) {\n const contentManagerContentTypeService = strapi\n .plugin('content-manager')\n .service('content-types');\n\n const contentTypesData: Record<UID.ContentType, { 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 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 contentTypeModelsMap = contentTypeUids.reduce(\n (\n acc: { [key: ReleaseAction['contentType']]: Schema.ContentType },\n contentTypeUid: ReleaseAction['contentType']\n ) => {\n acc[contentTypeUid] = strapi.getModel(contentTypeUid);\n\n return acc;\n },\n {}\n );\n\n return contentTypeModelsMap;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi.plugin('content-manager').service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (acc: { [key: Schema.Component['uid']]: Schema.Component }, component: Schema.Component) => {\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 = (await strapi.entityService.findOne(RELEASE_MODEL_UID, releaseId, {\n populate: {\n actions: {\n fields: ['id'],\n },\n },\n })) as unknown as Release;\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 await strapi.entityService.delete(RELEASE_MODEL_UID, releaseId);\n });\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n // We need to pass the type because entityService.findOne is not returning the correct type\n const releaseWithPopulatedActionEntries = (await strapi.entityService.findOne(\n RELEASE_MODEL_UID,\n releaseId,\n {\n populate: {\n actions: {\n populate: {\n entry: {\n fields: ['id'],\n },\n },\n },\n },\n }\n )) as unknown as Release;\n\n if (!releaseWithPopulatedActionEntries) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (releaseWithPopulatedActionEntries.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (releaseWithPopulatedActionEntries.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 IDs. Then we can fetch more data for each entry\n * We need to separate collectionTypes from singleTypes because findMany work as findOne for singleTypes and publishMany can't be used for singleTypes\n */\n const collectionTypeActions: {\n [key: UID.ContentType]: {\n entriestoPublishIds: ReleaseAction['entry']['id'][];\n entriesToUnpublishIds: ReleaseAction['entry']['id'][];\n };\n } = {};\n const singleTypeActions: {\n uid: UID.ContentType;\n id: ReleaseAction['entry']['id'];\n action: ReleaseAction['type'];\n }[] = [];\n for (const action of releaseWithPopulatedActionEntries.actions) {\n const contentTypeUid = action.contentType;\n\n if (strapi.contentTypes[contentTypeUid].kind === 'collectionType') {\n if (!collectionTypeActions[contentTypeUid]) {\n collectionTypeActions[contentTypeUid] = {\n entriestoPublishIds: [],\n entriesToUnpublishIds: [],\n };\n }\n\n if (action.type === 'publish') {\n collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);\n } else {\n collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);\n }\n } else {\n singleTypeActions.push({\n uid: contentTypeUid,\n action: action.type,\n id: action.entry.id,\n });\n }\n }\n\n const entityManagerService = strapi.plugin('content-manager').service('entity-manager');\n const populateBuilderService = strapi.plugin('content-manager').service('populate-builder');\n\n // Only publish the release if all action updates are applied successfully to their entry, otherwise leave everything as is\n await strapi.db.transaction(async () => {\n // First we publish all the singleTypes\n for (const { uid, action, id } of singleTypeActions) {\n // @ts-expect-error - populateBuilderService should be a function but is returning service\n const populate = await populateBuilderService(uid).populateDeep(Infinity).build();\n\n const entry = await strapi.entityService.findOne(uid, id, { populate });\n\n try {\n if (action === 'publish') {\n await entityManagerService.publish(entry, uid);\n } else {\n await entityManagerService.unpublish(entry, uid);\n }\n } catch (error) {\n if (\n error instanceof errors.ApplicationError &&\n (error.message === 'already.published' || error.message === 'already.draft')\n ) {\n // We don't want throw an error if the entry is already published or draft\n } else {\n throw error;\n }\n }\n }\n\n // Then, we can continue with publishing the collectionTypes\n for (const contentTypeUid of Object.keys(collectionTypeActions)) {\n // @ts-expect-error - populateBuilderService should be a function but is returning service\n const populate = await populateBuilderService(contentTypeUid)\n .populateDeep(Infinity)\n .build();\n\n const { entriestoPublishIds, entriesToUnpublishIds } =\n collectionTypeActions[contentTypeUid as UID.ContentType];\n\n /**\n * We need to get the populate entries to be able to publish without errors on components/relations/dynamicZones\n * Considering that populate doesn't work well with morph relations we can't get the entries from the Release model\n * So, we need to fetch them manually\n */\n const entriesToPublish = (await strapi.entityService.findMany(\n contentTypeUid as UID.ContentType,\n {\n filters: {\n id: {\n $in: entriestoPublishIds,\n },\n },\n populate,\n }\n )) as Entity[];\n\n const entriesToUnpublish = (await strapi.entityService.findMany(\n contentTypeUid as UID.ContentType,\n {\n filters: {\n id: {\n $in: entriesToUnpublishIds,\n },\n },\n populate,\n }\n )) as Entity[];\n\n if (entriesToPublish.length > 0) {\n await entityManagerService.publishMany(entriesToPublish, contentTypeUid);\n }\n\n if (entriesToUnpublish.length > 0) {\n await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);\n }\n }\n });\n\n // When the transaction fails it throws an error, when it is successful proceed to updating the release\n const release = await strapi.entityService.update(RELEASE_MODEL_UID, releaseId, {\n data: {\n /*\n * The type returned from the entity service: Partial<Input<\"plugin::content-releases.release\">> looks like it's wrong\n */\n // @ts-expect-error see above\n releasedAt: new Date(),\n },\n });\n\n return release;\n },\n\n async updateAction(\n actionId: UpdateReleaseAction.Request['params']['actionId'],\n releaseId: UpdateReleaseAction.Request['params']['releaseId'],\n update: UpdateReleaseAction.Request['body']\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: update,\n });\n\n if (!updatedAction) {\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 return updatedAction;\n },\n\n async deleteAction(\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 return deletedAction;\n },\n});\n\nexport default createReleaseService;\n","import { errors } from '@strapi/utils';\nimport { LoadedStrapi } from '@strapi/types';\nimport EE from '@strapi/strapi/dist/utils/ee';\nimport type { Release, CreateRelease } from '../../../shared/contracts/releases';\nimport type { CreateReleaseAction } from '../../../shared/contracts/release-actions';\nimport { RELEASE_MODEL_UID } from '../constants';\n\nconst createReleaseValidationService = ({ strapi }: { strapi: LoadedStrapi }) => ({\n async validateUniqueEntry(\n releaseId: CreateReleaseAction.Request['params']['releaseId'],\n releaseActionArgs: CreateReleaseAction.Request['body']\n ) {\n /**\n * Asserting the type, otherwise TS complains: 'release.actions' is of type 'unknown', even though the types come through for non-populated fields...\n * Possibly related to the comment on GetValues: https://github.com/strapi/strapi/blob/main/packages/core/types/src/modules/entity-service/result.ts\n */\n const release = (await strapi.entityService.findOne(RELEASE_MODEL_UID, releaseId, {\n populate: { actions: { populate: { entry: { fields: ['id'] } } } },\n })) as Release | null;\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const isEntryInRelease = release.actions.some(\n (action) =>\n Number(action.entry.id) === Number(releaseActionArgs.entry.id) &&\n action.contentType === releaseActionArgs.entry.contentType\n );\n\n if (isEntryInRelease) {\n throw new errors.ValidationError(\n `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`\n );\n }\n },\n validateEntryContentType(\n contentTypeUid: CreateReleaseAction.Request['body']['entry']['contentType']\n ) {\n const contentType = strapi.contentType(contentTypeUid);\n\n if (!contentType) {\n throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);\n }\n\n // TODO: V5 migration - All contentType will have draftAndPublish enabled\n if (!contentType.options?.draftAndPublish) {\n throw new errors.ValidationError(\n `Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`\n );\n }\n },\n async validatePendingReleasesLimit() {\n // Use the maximum releases option if it exists, otherwise default to 3\n const maximumPendingReleases =\n // @ts-expect-error - options is not typed into features\n EE.features.get('cms-content-releases')?.options?.maximumReleases || 3;\n\n const [, pendingReleasesCount] = await strapi.db.query(RELEASE_MODEL_UID).findWithCount({\n filters: {\n releasedAt: {\n $null: true,\n },\n },\n });\n\n // Unlimited is a number that will never be reached like 9999\n if (pendingReleasesCount >= maximumPendingReleases) {\n throw new errors.ValidationError('You have reached the maximum number of pending releases');\n }\n },\n async validateUniqueNameForPendingRelease(name: CreateRelease.Request['body']['name']) {\n const pendingReleases = (await strapi.entityService.findMany(RELEASE_MODEL_UID, {\n filters: {\n releasedAt: {\n $null: true,\n },\n name,\n },\n })) as Release[];\n\n const isNameUnique = pendingReleases.length === 0;\n\n if (!isNameUnique) {\n throw new errors.ValidationError(`Release with name ${name} already exists`);\n }\n },\n});\n\nexport default createReleaseValidationService;\n","import release from './release';\nimport releaseValidation from './validation';\n\nexport const services = {\n release,\n 'release-validation': releaseValidation,\n};\n","import * as yup from 'yup';\n\nexport const RELEASE_SCHEMA = yup\n .object()\n .shape({\n name: yup.string().trim().required(),\n })\n .required()\n .noUnknown();\n","import { validateYupSchema } from '@strapi/utils';\nimport { RELEASE_SCHEMA } from '../../../../shared/validation-schemas';\n\nexport const validateRelease = validateYupSchema(RELEASE_SCHEMA);\n","import type Koa from 'koa';\nimport { errors } from '@strapi/utils';\nimport { RELEASE_MODEL_UID } from '../constants';\nimport { validateRelease } from './validation/release';\nimport type {\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n GetContentTypeEntryReleases,\n GetReleases,\n} from '../../../shared/contracts/releases';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService } from '../utils';\n\ntype ReleaseWithPopulatedActions = Release & { actions: { count: number } };\n\nconst releaseController = {\n async findMany(ctx: Koa.Context) {\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_MODEL_UID,\n });\n\n await permissionsManager.validateQuery(ctx.query);\n\n const releaseService = getService('release', { strapi });\n\n // Handle requests for releases filtered by content type entry\n const isFindManyForContentTypeEntry = Boolean(ctx.query?.contentTypeUid && ctx.query?.entryId);\n if (isFindManyForContentTypeEntry) {\n const query: GetContentTypeEntryReleases.Request['query'] =\n await permissionsManager.sanitizeQuery(ctx.query);\n\n const contentTypeUid = query.contentTypeUid;\n const entryId = query.entryId;\n // Parse the string value or fallback to a default\n const hasEntryAttached: GetContentTypeEntryReleases.Request['query']['hasEntryAttached'] =\n typeof query.hasEntryAttached === 'string' ? JSON.parse(query.hasEntryAttached) : false;\n\n const data = hasEntryAttached\n ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId)\n : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);\n\n ctx.body = { data };\n } else {\n const query: GetReleases.Request['query'] = await permissionsManager.sanitizeQuery(ctx.query);\n const { results, pagination } = await releaseService.findPage(query);\n\n const data = results.map((release: ReleaseWithPopulatedActions) => {\n const { actions, ...releaseData } = release;\n\n return {\n ...releaseData,\n actions: {\n meta: {\n count: actions.count,\n },\n },\n };\n });\n\n ctx.body = { data, meta: { pagination } };\n }\n },\n\n async findOne(ctx: Koa.Context) {\n const id: GetRelease.Request['params']['id'] = ctx.params.id;\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.findOne(id, { populate: ['createdBy'] });\n if (!release) {\n throw new errors.NotFoundError(`Release not found for id: ${id}`);\n }\n\n const count = await releaseService.countActions({\n filters: {\n release: id,\n },\n });\n const sanitizedRelease = {\n ...release,\n createdBy: release.createdBy\n ? strapi.admin.services.user.sanitizeUser(release.createdBy)\n : null,\n };\n\n // Format the data object\n const data = {\n ...sanitizedRelease,\n actions: {\n meta: {\n count,\n },\n },\n };\n\n ctx.body = { data };\n },\n\n async create(ctx: Koa.Context) {\n const user: UserInfo = ctx.state.user;\n const releaseArgs: CreateRelease.Request['body'] = ctx.request.body;\n\n await validateRelease(releaseArgs);\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.create(releaseArgs, { user });\n\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_MODEL_UID,\n });\n\n ctx.body = {\n data: await permissionsManager.sanitizeOutput(release),\n };\n },\n\n async update(ctx: Koa.Context) {\n const user: UserInfo = ctx.state.user;\n const releaseArgs: UpdateRelease.Request['body'] = ctx.request.body;\n const id: UpdateRelease.Request['params']['id'] = ctx.params.id;\n\n await validateRelease(releaseArgs);\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.update(id, releaseArgs, { user });\n\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_MODEL_UID,\n });\n\n ctx.body = {\n data: await permissionsManager.sanitizeOutput(release),\n };\n },\n\n async delete(ctx: Koa.Context) {\n const id: DeleteRelease.Request['params']['id'] = ctx.params.id;\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.delete(id);\n\n ctx.body = {\n data: release,\n };\n },\n\n async publish(ctx: Koa.Context) {\n const user: PublishRelease.Request['state']['user'] = ctx.state.user;\n const id: PublishRelease.Request['params']['id'] = ctx.params.id;\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.publish(id, { user });\n\n const [countPublishActions, countUnpublishActions] = await Promise.all([\n releaseService.countActions({\n filters: {\n release: id,\n type: 'publish',\n },\n }),\n releaseService.countActions({\n filters: {\n release: id,\n type: 'unpublish',\n },\n }),\n ]);\n\n ctx.body = {\n data: release,\n meta: {\n totalEntries: countPublishActions + countUnpublishActions,\n totalPublishedEntries: countPublishActions,\n totalUnpublishedEntries: countUnpublishActions,\n },\n };\n },\n};\n\nexport default releaseController;\n","import { yup, validateYupSchema } from '@strapi/utils';\n\nconst RELEASE_ACTION_SCHEMA = yup.object().shape({\n entry: yup\n .object()\n .shape({\n id: yup.strapiID().required(),\n contentType: yup.string().required(),\n })\n .required(),\n type: yup.string().oneOf(['publish', 'unpublish']).required(),\n});\n\nconst RELEASE_ACTION_UPDATE_SCHEMA = yup.object().shape({\n type: yup.string().oneOf(['publish', 'unpublish']).required(),\n});\n\nexport const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);\nexport const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);\n","import type Koa from 'koa';\n\nimport { mapAsync } from '@strapi/utils';\nimport {\n validateReleaseAction,\n validateReleaseActionUpdateSchema,\n} from './validation/release-action';\nimport type {\n CreateReleaseAction,\n GetReleaseActions,\n UpdateReleaseAction,\n DeleteReleaseAction,\n} from '../../../shared/contracts/release-actions';\nimport { getService } from '../utils';\nimport { RELEASE_ACTION_MODEL_UID } from '../constants';\n\nconst releaseActionController = {\n async create(ctx: Koa.Context) {\n const releaseId: CreateReleaseAction.Request['params']['releaseId'] = ctx.params.releaseId;\n const releaseActionArgs: CreateReleaseAction.Request['body'] = ctx.request.body;\n\n await validateReleaseAction(releaseActionArgs);\n\n const releaseService = getService('release', { strapi });\n const releaseAction = await releaseService.createAction(releaseId, releaseActionArgs);\n\n ctx.body = {\n data: releaseAction,\n };\n },\n\n async findMany(ctx: Koa.Context) {\n const releaseId: GetReleaseActions.Request['params']['releaseId'] = ctx.params.releaseId;\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_ACTION_MODEL_UID,\n });\n const query = await permissionsManager.sanitizeQuery(ctx.query);\n\n const releaseService = getService('release', { strapi });\n const { results, pagination } = await releaseService.findActions(releaseId, {\n sort: query.groupBy === 'action' ? 'type' : query.groupBy,\n ...query,\n });\n\n /**\n * Release actions can be related to entries of different content types.\n * We need to sanitize the entry output according to that content type.\n * So, we group the sanitized output function by content type.\n */\n const contentTypeOutputSanitizers = results.reduce((acc, action) => {\n if (acc[action.contentType]) {\n return acc;\n }\n\n const contentTypePermissionsManager =\n strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: action.contentType,\n });\n\n acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;\n\n return acc;\n }, {});\n\n /**\n * sanitizeOutput doesn't work if you use it directly on the Release Action model, it doesn't sanitize the entries\n * So, we need to sanitize manually each entry inside a Release Action\n */\n const sanitizedResults = await mapAsync(results, async (action) => ({\n ...action,\n entry: await contentTypeOutputSanitizers[action.contentType](action.entry),\n }));\n\n const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);\n\n const contentTypes = releaseService.getContentTypeModelsFromActions(results);\n const components = await releaseService.getAllComponents();\n\n ctx.body = {\n data: groupedData,\n meta: {\n pagination,\n contentTypes,\n components,\n },\n };\n },\n\n async update(ctx: Koa.Context) {\n const actionId: UpdateReleaseAction.Request['params']['actionId'] = ctx.params.actionId;\n const releaseId: UpdateReleaseAction.Request['params']['releaseId'] = ctx.params.releaseId;\n const releaseActionUpdateArgs: UpdateReleaseAction.Request['body'] = ctx.request.body;\n\n await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);\n\n const releaseService = getService('release', { strapi });\n\n const updatedAction = await releaseService.updateAction(\n actionId,\n releaseId,\n releaseActionUpdateArgs\n );\n\n ctx.body = {\n data: updatedAction,\n };\n },\n\n async delete(ctx: Koa.Context) {\n const actionId: DeleteReleaseAction.Request['params']['actionId'] = ctx.params.actionId;\n const releaseId: DeleteReleaseAction.Request['params']['releaseId'] = ctx.params.releaseId;\n\n const releaseService = getService('release', { strapi });\n\n const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);\n\n ctx.body = {\n data: deletedReleaseAction,\n };\n },\n};\n\nexport default releaseActionController;\n","import release from './release';\nimport releaseAction from './release-action';\n\nexport const controllers = { release, 'release-action': releaseAction };\n","export default {\n type: 'admin',\n routes: [\n {\n method: 'POST',\n path: '/',\n handler: 'release.create',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.create'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/',\n handler: 'release.findMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.read'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/:id',\n handler: 'release.findOne',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.read'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/:id',\n handler: 'release.update',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.update'],\n },\n },\n ],\n },\n },\n {\n method: 'DELETE',\n path: '/:id',\n handler: 'release.delete',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.delete'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/:id/publish',\n handler: 'release.publish',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.publish'],\n },\n },\n ],\n },\n },\n ],\n};\n","export default {\n type: 'admin',\n routes: [\n {\n method: 'POST',\n path: '/:releaseId/actions',\n handler: 'release-action.create',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.create-action'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/:releaseId/actions',\n handler: 'release-action.findMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.read'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/:releaseId/actions/:actionId',\n handler: 'release-action.update',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.update'],\n },\n },\n ],\n },\n },\n {\n method: 'DELETE',\n path: '/:releaseId/actions/:actionId',\n handler: 'release-action.delete',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.delete-action'],\n },\n },\n ],\n },\n },\n ],\n};\n","import release from './release';\nimport releaseAction from './release-action';\n\nexport const routes = {\n release,\n 'release-action': releaseAction,\n};\n","/* eslint-disable @typescript-eslint/no-var-requires */\nimport { register } from './register';\nimport { bootstrap } from './bootstrap';\nimport { contentTypes } from './content-types';\nimport { services } from './services';\nimport { controllers } from './controllers';\nimport { routes } from './routes';\n\nconst { features } = require('@strapi/strapi/dist/utils/ee');\n\nconst getPlugin = () => {\n if (features.isEnabled('cms-content-releases')) {\n return {\n register,\n bootstrap,\n contentTypes,\n services,\n controllers,\n routes,\n };\n }\n\n // We keep returning contentTypes to avoid lost the data if feature is disabled\n return {\n contentTypes,\n };\n};\n\nexport default getPlugin();\n"],"names":["contentTypes","contentTypesUtils","difference","keys","mapAsync","features","strapi","release","schema","releaseAction","_","setCreatorFields","errors","EE","releaseValidation","yup","validateYupSchema"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../server/src/constants.ts","../../server/src/migrations/index.ts","../../server/src/register.ts","../../server/src/utils/index.ts","../../server/src/bootstrap.ts","../../server/src/destroy.ts","../../server/src/content-types/release/schema.ts","../../server/src/content-types/release/index.ts","../../server/src/content-types/release-action/schema.ts","../../server/src/content-types/release-action/index.ts","../../server/src/content-types/index.ts","../../server/src/services/release.ts","../../server/src/services/validation.ts","../../server/src/services/scheduling.ts","../../server/src/services/index.ts","../../shared/validation-schemas.ts","../../server/src/controllers/validation/release.ts","../../server/src/controllers/release.ts","../../server/src/controllers/validation/release-action.ts","../../server/src/controllers/release-action.ts","../../server/src/controllers/index.ts","../../server/src/routes/release.ts","../../server/src/routes/release-action.ts","../../server/src/routes/index.ts","../../server/src/index.ts"],"sourcesContent":["export const RELEASE_MODEL_UID = 'plugin::content-releases.release';\nexport const RELEASE_ACTION_MODEL_UID = 'plugin::content-releases.release-action';\n\nexport const ACTIONS = [\n {\n section: 'plugins',\n displayName: 'Read',\n uid: 'read',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Create',\n uid: 'create',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Edit',\n uid: 'update',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Delete',\n uid: 'delete',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Publish',\n uid: 'publish',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Remove an entry from a release',\n uid: 'delete-action',\n pluginName: 'content-releases',\n },\n {\n section: 'plugins',\n displayName: 'Add an entry to a release',\n uid: 'create-action',\n pluginName: 'content-releases',\n },\n];\n","import type { Schema } from '@strapi/types';\nimport { contentTypes as contentTypesUtils, mapAsync } from '@strapi/utils';\n\nimport { difference, keys } from 'lodash';\nimport { RELEASE_ACTION_MODEL_UID } from '../constants';\n\ninterface Input {\n oldContentTypes: Record<string, Schema.ContentType>;\n contentTypes: Record<string, Schema.ContentType>;\n}\n\nexport async function deleteActionsOnDisableDraftAndPublish({\n oldContentTypes,\n contentTypes,\n}: Input) {\n if (!oldContentTypes) {\n return;\n }\n\n for (const uid in contentTypes) {\n if (!oldContentTypes[uid]) {\n continue;\n }\n\n const oldContentType = oldContentTypes[uid];\n const contentType = contentTypes[uid];\n\n if (\n contentTypesUtils.hasDraftAndPublish(oldContentType) &&\n !contentTypesUtils.hasDraftAndPublish(contentType)\n ) {\n await strapi.db\n ?.queryBuilder(RELEASE_ACTION_MODEL_UID)\n .delete()\n .where({ contentType: uid })\n .execute();\n }\n }\n}\n\nexport async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes }: Input) {\n const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes)) ?? [];\n\n if (deletedContentTypes.length) {\n await mapAsync(deletedContentTypes, async (deletedContentTypeUID: unknown) => {\n return strapi.db\n ?.queryBuilder(RELEASE_ACTION_MODEL_UID)\n .delete()\n .where({ contentType: deletedContentTypeUID })\n .execute();\n });\n }\n}\n","/* eslint-disable @typescript-eslint/no-var-requires */\nimport type { LoadedStrapi } from '@strapi/types';\n\nimport { ACTIONS } from './constants';\nimport {\n deleteActionsOnDeleteContentType,\n deleteActionsOnDisableDraftAndPublish,\n} from './migrations';\n\nconst { features } = require('@strapi/strapi/dist/utils/ee');\n\nexport const register = async ({ strapi }: { strapi: LoadedStrapi }) => {\n if (features.isEnabled('cms-content-releases')) {\n await strapi.admin.services.permission.actionProvider.registerMany(ACTIONS);\n\n strapi.hook('strapi::content-types.beforeSync').register(deleteActionsOnDisableDraftAndPublish);\n strapi.hook('strapi::content-types.afterSync').register(deleteActionsOnDeleteContentType);\n }\n};\n","export const getService = (\n name: 'release' | 'release-validation' | 'scheduling' | 'release-action' | 'event-manager',\n { strapi } = { strapi: global.strapi }\n) => {\n return strapi.plugin('content-releases').service(name);\n};\n","/* eslint-disable @typescript-eslint/no-var-requires */\nimport type { LoadedStrapi, Entity as StrapiEntity } from '@strapi/types';\n\nimport { RELEASE_ACTION_MODEL_UID } from './constants';\nimport { getService } from './utils';\n\nconst { features } = require('@strapi/strapi/dist/utils/ee');\n\nexport const bootstrap = async ({ strapi }: { strapi: LoadedStrapi }) => {\n if (features.isEnabled('cms-content-releases')) {\n // Clean up release-actions when an entry is deleted\n strapi.db.lifecycles.subscribe({\n afterDelete(event) {\n // @ts-expect-error TODO: lifecycles types looks like are not 100% finished\n const { model, result } = event;\n // @ts-expect-error TODO: lifecycles types looks like are not 100% finished\n if (model.kind === 'collectionType' && model.options?.draftAndPublish) {\n const { id } = result;\n strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n target_type: model.uid,\n target_id: id,\n },\n });\n }\n },\n /**\n * deleteMany hook doesn't return the deleted entries ids\n * so we need to fetch them before deleting the entries to save the ids on our state\n */\n async beforeDeleteMany(event) {\n const { model, params } = event;\n // @ts-expect-error TODO: lifecycles types looks like are not 100% finished\n if (model.kind === 'collectionType' && model.options?.draftAndPublish) {\n const { where } = params;\n const entriesToDelete = await strapi.db\n .query(model.uid)\n .findMany({ select: ['id'], where });\n event.state.entriesToDelete = entriesToDelete;\n }\n },\n /**\n * We delete the release actions related to deleted entries\n * We make this only after deleteMany is succesfully executed to avoid errors\n */\n async afterDeleteMany(event) {\n const { model, state } = event;\n const entriesToDelete = state.entriesToDelete;\n if (entriesToDelete) {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n target_type: model.uid,\n target_id: {\n $in: (entriesToDelete as Array<{ id: StrapiEntity.ID }>).map((entry) => entry.id),\n },\n },\n });\n }\n },\n });\n\n if (strapi.features.future.isEnabled('contentReleasesScheduling')) {\n getService('scheduling', { strapi })\n .syncFromDatabase()\n .catch((err: Error) => {\n strapi.log.error(\n 'Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling.'\n );\n\n throw err;\n });\n }\n }\n};\n","import { Job } from 'node-schedule';\nimport { LoadedStrapi } from '@strapi/types';\n\nimport { Release } from '../../shared/contracts/releases';\nimport { getService } from './utils';\n\nexport const destroy = async ({ strapi }: { strapi: LoadedStrapi }) => {\n if (strapi.features.future.isEnabled('contentReleasesScheduling')) {\n const scheduledJobs: Map<Release['id'], Job> = getService('scheduling', {\n strapi,\n }).getAll();\n\n for (const [, job] of scheduledJobs) {\n job.cancel();\n }\n }\n};\n","import { RELEASE_ACTION_MODEL_UID } from '../../constants';\n\nexport default {\n collectionName: 'strapi_releases',\n info: {\n singularName: 'release',\n pluralName: 'releases',\n displayName: 'Release',\n },\n options: {\n draftAndPublish: false,\n },\n pluginOptions: {\n 'content-manager': {\n visible: false,\n },\n 'content-type-builder': {\n visible: false,\n },\n },\n attributes: {\n name: {\n type: 'string',\n required: true,\n },\n releasedAt: {\n type: 'datetime',\n },\n scheduledAt: {\n type: 'datetime',\n },\n timezone: {\n type: 'string',\n },\n actions: {\n type: 'relation',\n relation: 'oneToMany',\n target: RELEASE_ACTION_MODEL_UID,\n mappedBy: 'release',\n },\n },\n};\n","import schema from './schema';\n\nexport const release = {\n schema,\n};\n","import { RELEASE_MODEL_UID } from '../../constants';\n\nexport default {\n collectionName: 'strapi_release_actions',\n info: {\n singularName: 'release-action',\n pluralName: 'release-actions',\n displayName: 'Release Action',\n },\n options: {\n draftAndPublish: false,\n },\n pluginOptions: {\n 'content-manager': {\n visible: false,\n },\n 'content-type-builder': {\n visible: false,\n },\n },\n attributes: {\n type: {\n type: 'enumeration',\n enum: ['publish', 'unpublish'],\n required: true,\n },\n entry: {\n type: 'relation',\n relation: 'morphToOne',\n configurable: false,\n },\n contentType: {\n type: 'string',\n required: true,\n },\n locale: {\n type: 'string',\n },\n release: {\n type: 'relation',\n relation: 'manyToOne',\n target: RELEASE_MODEL_UID,\n inversedBy: 'actions',\n },\n },\n};\n","import schema from './schema';\n\nexport const releaseAction = {\n schema,\n};\n","import { release } from './release';\nimport { releaseAction } from './release-action';\n\nexport const contentTypes = {\n release,\n 'release-action': releaseAction,\n};\n","import { setCreatorFields, errors } from '@strapi/utils';\n\nimport type { LoadedStrapi, EntityService, UID, Schema } from '@strapi/types';\n\nimport _ from 'lodash/fp';\n\nimport { 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 GetContentTypeEntryReleases,\n} from '../../../shared/contracts/releases';\nimport type {\n CreateReleaseAction,\n GetReleaseActions,\n ReleaseAction,\n UpdateReleaseAction,\n DeleteReleaseAction,\n ReleaseActionGroupBy,\n} from '../../../shared/contracts/release-actions';\nimport type { Entity, UserInfo } from '../../../shared/types';\nimport { getService } from '../utils';\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 getGroupName = (queryValue?: ReleaseActionGroupBy) => {\n switch (queryValue) {\n case 'contentType':\n return 'contentType.displayName';\n case 'action':\n return 'type';\n case 'locale':\n return _.getOr('No locale', 'locale.name');\n default:\n return 'contentType.displayName';\n }\n};\n\nconst createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({\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.entityService.create(RELEASE_MODEL_UID, {\n data: releaseWithCreatorFields,\n });\n\n if (\n strapi.features.future.isEnabled('contentReleasesScheduling') &&\n releaseWithCreatorFields.scheduledAt\n ) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const release = await strapi.entityService.findOne(RELEASE_MODEL_UID, id, {\n ...query,\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n return strapi.entityService.findPage(RELEASE_MODEL_UID, {\n ...query,\n populate: {\n actions: {\n // @ts-expect-error Ignore missing properties\n count: true,\n },\n },\n });\n },\n\n async findManyWithContentTypeEntryAttached(\n contentTypeUid: GetContentTypeEntryReleases.Request['query']['contentTypeUid'],\n entryId: GetContentTypeEntryReleases.Request['query']['entryId']\n ) {\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n actions: {\n target_type: contentTypeUid,\n target_id: entryId,\n },\n releasedAt: {\n $null: true,\n },\n },\n populate: {\n // Filter the action to get only the content type entry\n actions: {\n where: {\n target_type: contentTypeUid,\n target_id: entryId,\n },\n },\n },\n });\n\n return releases.map((release) => {\n if (release.actions?.length) {\n const [actionForEntry] = release.actions;\n\n // Remove the actions key to replace it with an action key\n delete release.actions;\n\n return {\n ...release,\n action: actionForEntry,\n };\n }\n\n return release;\n });\n },\n\n async findManyWithoutContentTypeEntryAttached(\n contentTypeUid: GetContentTypeEntryReleases.Request['query']['contentTypeUid'],\n entryId: GetContentTypeEntryReleases.Request['query']['entryId']\n ) {\n // We get the list of releases where the entry is present\n const releasesRelated = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n releasedAt: {\n $null: true,\n },\n actions: {\n target_type: contentTypeUid,\n target_id: entryId,\n },\n },\n });\n\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n $or: [\n {\n id: {\n $notIn: releasesRelated.map((release) => release.id),\n },\n },\n {\n actions: null,\n },\n ],\n releasedAt: {\n $null: true,\n },\n },\n });\n\n return releases.map((release) => {\n if (release.actions?.length) {\n const [actionForEntry] = release.actions;\n\n // Remove the actions key to replace it with an action key\n delete release.actions;\n\n return {\n ...release,\n action: actionForEntry,\n };\n }\n\n return release;\n });\n },\n\n async update(\n id: number,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(releaseData);\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.entityService.findOne(RELEASE_MODEL_UID, 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.entityService.update(RELEASE_MODEL_UID, id, {\n /*\n * The type returned from the entity service: Partial<Input<\"plugin::content-releases.release\">>\n * is not compatible with the type we are passing here: UpdateRelease.Request['body']\n */\n // @ts-expect-error see above\n data: releaseWithCreatorFields,\n });\n\n if (strapi.features.future.isEnabled('contentReleasesScheduling')) {\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\n return updatedRelease;\n },\n\n async createAction(\n releaseId: CreateReleaseAction.Request['params']['releaseId'],\n action: Pick<CreateReleaseAction.Request['body'], 'type' | 'entry'>\n ) {\n const { validateEntryContentType, validateUniqueEntry } = getService('release-validation', {\n strapi,\n });\n\n await Promise.all([\n validateEntryContentType(action.entry.contentType),\n validateUniqueEntry(releaseId, action),\n ]);\n\n const release = await strapi.entityService.findOne(RELEASE_MODEL_UID, 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 const { entry, type } = action;\n\n return strapi.entityService.create(RELEASE_ACTION_MODEL_UID, {\n data: {\n type,\n contentType: entry.contentType,\n locale: entry.locale,\n entry: {\n id: entry.id,\n __type: entry.contentType,\n __pivot: { field: 'entry' },\n },\n release: releaseId,\n },\n populate: { release: { fields: ['id'] }, entry: { fields: ['id'] } },\n });\n },\n\n async findActions(\n releaseId: GetReleaseActions.Request['params']['releaseId'],\n query?: GetReleaseActions.Request['query']\n ) {\n const release = await strapi.entityService.findOne(RELEASE_MODEL_UID, releaseId, {\n fields: ['id'],\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n return strapi.entityService.findPage(RELEASE_ACTION_MODEL_UID, {\n ...query,\n populate: {\n entry: {\n populate: '*',\n },\n },\n filters: {\n release: releaseId,\n },\n });\n },\n\n async countActions(query: EntityService.Params.Pick<typeof RELEASE_ACTION_MODEL_UID, 'filters'>) {\n return strapi.entityService.count(RELEASE_ACTION_MODEL_UID, query);\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 this.getContentTypesDataForActions(\n contentTypeUids\n );\n const allLocalesDictionary = await this.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 getLocalesDataForActions() {\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 async getContentTypesDataForActions(contentTypesUids: ReleaseAction['contentType'][]) {\n const contentManagerContentTypeService = strapi\n .plugin('content-manager')\n .service('content-types');\n\n const contentTypesData: Record<UID.ContentType, { 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 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 contentTypeModelsMap = contentTypeUids.reduce(\n (\n acc: { [key: ReleaseAction['contentType']]: Schema.ContentType },\n contentTypeUid: ReleaseAction['contentType']\n ) => {\n acc[contentTypeUid] = strapi.getModel(contentTypeUid);\n\n return acc;\n },\n {}\n );\n\n return contentTypeModelsMap;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi.plugin('content-manager').service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (acc: { [key: Schema.Component['uid']]: Schema.Component }, component: Schema.Component) => {\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 = (await strapi.entityService.findOne(RELEASE_MODEL_UID, releaseId, {\n populate: {\n actions: {\n fields: ['id'],\n },\n },\n })) as unknown as Release;\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 await strapi.entityService.delete(RELEASE_MODEL_UID, releaseId);\n });\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n // We need to pass the type because entityService.findOne is not returning the correct type\n const releaseWithPopulatedActionEntries = (await strapi.entityService.findOne(\n RELEASE_MODEL_UID,\n releaseId,\n {\n populate: {\n actions: {\n populate: {\n entry: {\n fields: ['id'],\n },\n },\n },\n },\n }\n )) as unknown as Release;\n\n if (!releaseWithPopulatedActionEntries) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (releaseWithPopulatedActionEntries.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (releaseWithPopulatedActionEntries.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 IDs. Then we can fetch more data for each entry\n * We need to separate collectionTypes from singleTypes because findMany work as findOne for singleTypes and publishMany can't be used for singleTypes\n */\n const collectionTypeActions: {\n [key: UID.ContentType]: {\n entriestoPublishIds: ReleaseAction['entry']['id'][];\n entriesToUnpublishIds: ReleaseAction['entry']['id'][];\n };\n } = {};\n const singleTypeActions: {\n uid: UID.ContentType;\n id: ReleaseAction['entry']['id'];\n action: ReleaseAction['type'];\n }[] = [];\n for (const action of releaseWithPopulatedActionEntries.actions) {\n const contentTypeUid = action.contentType;\n\n if (strapi.contentTypes[contentTypeUid].kind === 'collectionType') {\n if (!collectionTypeActions[contentTypeUid]) {\n collectionTypeActions[contentTypeUid] = {\n entriestoPublishIds: [],\n entriesToUnpublishIds: [],\n };\n }\n\n if (action.type === 'publish') {\n collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);\n } else {\n collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);\n }\n } else {\n singleTypeActions.push({\n uid: contentTypeUid,\n action: action.type,\n id: action.entry.id,\n });\n }\n }\n\n const entityManagerService = strapi.plugin('content-manager').service('entity-manager');\n const populateBuilderService = strapi.plugin('content-manager').service('populate-builder');\n\n // Only publish the release if all action updates are applied successfully to their entry, otherwise leave everything as is\n await strapi.db.transaction(async () => {\n // First we publish all the singleTypes\n for (const { uid, action, id } of singleTypeActions) {\n // @ts-expect-error - populateBuilderService should be a function but is returning service\n const populate = await populateBuilderService(uid).populateDeep(Infinity).build();\n\n const entry = await strapi.entityService.findOne(uid, id, { populate });\n\n try {\n if (action === 'publish') {\n await entityManagerService.publish(entry, uid);\n } else {\n await entityManagerService.unpublish(entry, uid);\n }\n } catch (error) {\n if (\n error instanceof errors.ApplicationError &&\n (error.message === 'already.published' || error.message === 'already.draft')\n ) {\n // We don't want throw an error if the entry is already published or draft\n } else {\n throw error;\n }\n }\n }\n\n // Then, we can continue with publishing the collectionTypes\n for (const contentTypeUid of Object.keys(collectionTypeActions)) {\n // @ts-expect-error - populateBuilderService should be a function but is returning service\n const populate = await populateBuilderService(contentTypeUid)\n .populateDeep(Infinity)\n .build();\n\n const { entriestoPublishIds, entriesToUnpublishIds } =\n collectionTypeActions[contentTypeUid as UID.ContentType];\n\n /**\n * We need to get the populate entries to be able to publish without errors on components/relations/dynamicZones\n * Considering that populate doesn't work well with morph relations we can't get the entries from the Release model\n * So, we need to fetch them manually\n */\n const entriesToPublish = (await strapi.entityService.findMany(\n contentTypeUid as UID.ContentType,\n {\n filters: {\n id: {\n $in: entriestoPublishIds,\n },\n },\n populate,\n }\n )) as Entity[];\n\n const entriesToUnpublish = (await strapi.entityService.findMany(\n contentTypeUid as UID.ContentType,\n {\n filters: {\n id: {\n $in: entriesToUnpublishIds,\n },\n },\n populate,\n }\n )) as Entity[];\n\n if (entriesToPublish.length > 0) {\n await entityManagerService.publishMany(entriesToPublish, contentTypeUid);\n }\n\n if (entriesToUnpublish.length > 0) {\n await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);\n }\n }\n });\n\n // When the transaction fails it throws an error, when it is successful proceed to updating the release\n const release = await strapi.entityService.update(RELEASE_MODEL_UID, releaseId, {\n data: {\n /*\n * The type returned from the entity service: Partial<Input<\"plugin::content-releases.release\">> looks like it's wrong\n */\n // @ts-expect-error see above\n releasedAt: new Date(),\n },\n });\n\n return release;\n },\n\n async updateAction(\n actionId: UpdateReleaseAction.Request['params']['actionId'],\n releaseId: UpdateReleaseAction.Request['params']['releaseId'],\n update: UpdateReleaseAction.Request['body']\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: update,\n });\n\n if (!updatedAction) {\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 return updatedAction;\n },\n\n async deleteAction(\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 return deletedAction;\n },\n});\n\nexport default createReleaseService;\n","import { errors } from '@strapi/utils';\nimport { LoadedStrapi } from '@strapi/types';\nimport EE from '@strapi/strapi/dist/utils/ee';\nimport type { Release, CreateRelease, UpdateRelease } from '../../../shared/contracts/releases';\nimport type { CreateReleaseAction } from '../../../shared/contracts/release-actions';\nimport { RELEASE_MODEL_UID } from '../constants';\n\nconst createReleaseValidationService = ({ strapi }: { strapi: LoadedStrapi }) => ({\n async validateUniqueEntry(\n releaseId: CreateReleaseAction.Request['params']['releaseId'],\n releaseActionArgs: CreateReleaseAction.Request['body']\n ) {\n /**\n * Asserting the type, otherwise TS complains: 'release.actions' is of type 'unknown', even though the types come through for non-populated fields...\n * Possibly related to the comment on GetValues: https://github.com/strapi/strapi/blob/main/packages/core/types/src/modules/entity-service/result.ts\n */\n const release = (await strapi.entityService.findOne(RELEASE_MODEL_UID, releaseId, {\n populate: { actions: { populate: { entry: { fields: ['id'] } } } },\n })) as Release | null;\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const isEntryInRelease = release.actions.some(\n (action) =>\n Number(action.entry.id) === Number(releaseActionArgs.entry.id) &&\n action.contentType === releaseActionArgs.entry.contentType\n );\n\n if (isEntryInRelease) {\n throw new errors.ValidationError(\n `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`\n );\n }\n },\n validateEntryContentType(\n contentTypeUid: CreateReleaseAction.Request['body']['entry']['contentType']\n ) {\n const contentType = strapi.contentType(contentTypeUid);\n\n if (!contentType) {\n throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);\n }\n\n // TODO: V5 migration - All contentType will have draftAndPublish enabled\n if (!contentType.options?.draftAndPublish) {\n throw new errors.ValidationError(\n `Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`\n );\n }\n },\n async validatePendingReleasesLimit() {\n // Use the maximum releases option if it exists, otherwise default to 3\n const maximumPendingReleases =\n // @ts-expect-error - options is not typed into features\n EE.features.get('cms-content-releases')?.options?.maximumReleases || 3;\n\n const [, pendingReleasesCount] = await strapi.db.query(RELEASE_MODEL_UID).findWithCount({\n filters: {\n releasedAt: {\n $null: true,\n },\n },\n });\n\n // Unlimited is a number that will never be reached like 9999\n if (pendingReleasesCount >= maximumPendingReleases) {\n throw new errors.ValidationError('You have reached the maximum number of pending releases');\n }\n },\n async validateUniqueNameForPendingRelease(\n name: CreateRelease.Request['body']['name'],\n id?: UpdateRelease.Request['params']['id']\n ) {\n const pendingReleases = (await strapi.entityService.findMany(RELEASE_MODEL_UID, {\n filters: {\n releasedAt: {\n $null: true,\n },\n name,\n ...(id && { id: { $ne: id } }),\n },\n })) as Release[];\n\n const isNameUnique = pendingReleases.length === 0;\n\n if (!isNameUnique) {\n throw new errors.ValidationError(`Release with name ${name} already exists`);\n }\n },\n async validateScheduledAtIsLaterThanNow(\n scheduledAt: CreateRelease.Request['body']['scheduledAt']\n ) {\n if (scheduledAt && new Date(scheduledAt) <= new Date()) {\n throw new errors.ValidationError('Scheduled at must be later than now');\n }\n },\n});\n\nexport default createReleaseValidationService;\n","import { scheduleJob, Job } from 'node-schedule';\nimport { LoadedStrapi } from '@strapi/types';\n\nimport { errors } from '@strapi/utils';\nimport { Release } from '../../../shared/contracts/releases';\nimport { getService } from '../utils';\nimport { RELEASE_MODEL_UID } from '../constants';\n\nconst createSchedulingService = ({ strapi }: { strapi: LoadedStrapi }) => {\n const scheduledJobs = new Map<Release['id'], Job>();\n\n return {\n async set(releaseId: Release['id'], scheduleDate: Date) {\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId, releasedAt: null } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const job = scheduleJob(scheduleDate, async () => {\n try {\n await getService('release').publish(releaseId);\n // @TODO: Trigger webhook with success message\n } catch (error) {\n // @TODO: Trigger webhook with error message\n }\n\n this.cancel(releaseId);\n });\n\n if (scheduledJobs.has(releaseId)) {\n this.cancel(releaseId);\n }\n\n scheduledJobs.set(releaseId, job);\n\n return scheduledJobs;\n },\n\n cancel(releaseId: Release['id']) {\n if (scheduledJobs.has(releaseId)) {\n scheduledJobs.get(releaseId)!.cancel();\n scheduledJobs.delete(releaseId);\n }\n\n return scheduledJobs;\n },\n\n getAll() {\n return scheduledJobs;\n },\n\n /**\n * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released\n * This is useful in case the server was restarted and the scheduled jobs were lost\n * This also could be used to sync different Strapi instances in case of a cluster\n */\n async syncFromDatabase() {\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n scheduledAt: {\n $gte: new Date(),\n },\n releasedAt: null,\n },\n });\n\n for (const release of releases) {\n this.set(release.id, release.scheduledAt);\n }\n\n return scheduledJobs;\n },\n };\n};\n\nexport default createSchedulingService;\n","import release from './release';\nimport releaseValidation from './validation';\nimport scheduling from './scheduling';\n\nexport const services = {\n release,\n 'release-validation': releaseValidation,\n ...(strapi.features.future.isEnabled('contentReleasesScheduling') ? { scheduling } : {}),\n};\n","import * as yup from 'yup';\n\nexport const RELEASE_SCHEMA = yup\n .object()\n .shape({\n name: yup.string().trim().required(),\n // scheduledAt is a date, but we always receive strings from the client\n scheduledAt: yup.string().nullable(),\n timezone: yup.string().when('scheduledAt', {\n is: (scheduledAt: string) => !!scheduledAt,\n then: yup.string().required(),\n otherwise: yup.string().nullable(),\n }),\n })\n .required()\n .noUnknown();\n","import { validateYupSchema } from '@strapi/utils';\nimport { RELEASE_SCHEMA } from '../../../../shared/validation-schemas';\n\nexport const validateRelease = validateYupSchema(RELEASE_SCHEMA);\n","import type Koa from 'koa';\nimport { errors } from '@strapi/utils';\nimport { RELEASE_MODEL_UID } from '../constants';\nimport { validateRelease } from './validation/release';\nimport type {\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n GetContentTypeEntryReleases,\n GetReleases,\n} from '../../../shared/contracts/releases';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService } from '../utils';\n\ntype ReleaseWithPopulatedActions = Release & { actions: { count: number } };\n\nconst releaseController = {\n async findMany(ctx: Koa.Context) {\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_MODEL_UID,\n });\n\n await permissionsManager.validateQuery(ctx.query);\n\n const releaseService = getService('release', { strapi });\n\n // Handle requests for releases filtered by content type entry\n const isFindManyForContentTypeEntry = Boolean(ctx.query?.contentTypeUid && ctx.query?.entryId);\n if (isFindManyForContentTypeEntry) {\n const query: GetContentTypeEntryReleases.Request['query'] =\n await permissionsManager.sanitizeQuery(ctx.query);\n\n const contentTypeUid = query.contentTypeUid;\n const entryId = query.entryId;\n // Parse the string value or fallback to a default\n const hasEntryAttached: GetContentTypeEntryReleases.Request['query']['hasEntryAttached'] =\n typeof query.hasEntryAttached === 'string' ? JSON.parse(query.hasEntryAttached) : false;\n\n const data = hasEntryAttached\n ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId)\n : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);\n\n ctx.body = { data };\n } else {\n const query: GetReleases.Request['query'] = await permissionsManager.sanitizeQuery(ctx.query);\n const { results, pagination } = await releaseService.findPage(query);\n\n const data = results.map((release: ReleaseWithPopulatedActions) => {\n const { actions, ...releaseData } = release;\n\n return {\n ...releaseData,\n actions: {\n meta: {\n count: actions.count,\n },\n },\n };\n });\n\n ctx.body = { data, meta: { pagination } };\n }\n },\n\n async findOne(ctx: Koa.Context) {\n const id: GetRelease.Request['params']['id'] = ctx.params.id;\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.findOne(id, { populate: ['createdBy'] });\n if (!release) {\n throw new errors.NotFoundError(`Release not found for id: ${id}`);\n }\n\n const count = await releaseService.countActions({\n filters: {\n release: id,\n },\n });\n const sanitizedRelease = {\n ...release,\n createdBy: release.createdBy\n ? strapi.admin.services.user.sanitizeUser(release.createdBy)\n : null,\n };\n\n // Format the data object\n const data = {\n ...sanitizedRelease,\n actions: {\n meta: {\n count,\n },\n },\n };\n\n ctx.body = { data };\n },\n\n async create(ctx: Koa.Context) {\n const user: UserInfo = ctx.state.user;\n const releaseArgs: CreateRelease.Request['body'] = ctx.request.body;\n\n await validateRelease(releaseArgs);\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.create(releaseArgs, { user });\n\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_MODEL_UID,\n });\n\n ctx.body = {\n data: await permissionsManager.sanitizeOutput(release),\n };\n },\n\n async update(ctx: Koa.Context) {\n const user: UserInfo = ctx.state.user;\n const releaseArgs: UpdateRelease.Request['body'] = ctx.request.body;\n const id: UpdateRelease.Request['params']['id'] = ctx.params.id;\n\n await validateRelease(releaseArgs);\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.update(id, releaseArgs, { user });\n\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_MODEL_UID,\n });\n\n ctx.body = {\n data: await permissionsManager.sanitizeOutput(release),\n };\n },\n\n async delete(ctx: Koa.Context) {\n const id: DeleteRelease.Request['params']['id'] = ctx.params.id;\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.delete(id);\n\n ctx.body = {\n data: release,\n };\n },\n\n async publish(ctx: Koa.Context) {\n const user: PublishRelease.Request['state']['user'] = ctx.state.user;\n const id: PublishRelease.Request['params']['id'] = ctx.params.id;\n\n const releaseService = getService('release', { strapi });\n const release = await releaseService.publish(id, { user });\n\n const [countPublishActions, countUnpublishActions] = await Promise.all([\n releaseService.countActions({\n filters: {\n release: id,\n type: 'publish',\n },\n }),\n releaseService.countActions({\n filters: {\n release: id,\n type: 'unpublish',\n },\n }),\n ]);\n\n ctx.body = {\n data: release,\n meta: {\n totalEntries: countPublishActions + countUnpublishActions,\n totalPublishedEntries: countPublishActions,\n totalUnpublishedEntries: countUnpublishActions,\n },\n };\n },\n};\n\nexport default releaseController;\n","import { yup, validateYupSchema } from '@strapi/utils';\n\nconst RELEASE_ACTION_SCHEMA = yup.object().shape({\n entry: yup\n .object()\n .shape({\n id: yup.strapiID().required(),\n contentType: yup.string().required(),\n })\n .required(),\n type: yup.string().oneOf(['publish', 'unpublish']).required(),\n});\n\nconst RELEASE_ACTION_UPDATE_SCHEMA = yup.object().shape({\n type: yup.string().oneOf(['publish', 'unpublish']).required(),\n});\n\nexport const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);\nexport const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);\n","import type Koa from 'koa';\n\nimport { mapAsync } from '@strapi/utils';\nimport {\n validateReleaseAction,\n validateReleaseActionUpdateSchema,\n} from './validation/release-action';\nimport type {\n CreateReleaseAction,\n GetReleaseActions,\n UpdateReleaseAction,\n DeleteReleaseAction,\n} from '../../../shared/contracts/release-actions';\nimport { getService } from '../utils';\nimport { RELEASE_ACTION_MODEL_UID } from '../constants';\n\nconst releaseActionController = {\n async create(ctx: Koa.Context) {\n const releaseId: CreateReleaseAction.Request['params']['releaseId'] = ctx.params.releaseId;\n const releaseActionArgs: CreateReleaseAction.Request['body'] = ctx.request.body;\n\n await validateReleaseAction(releaseActionArgs);\n\n const releaseService = getService('release', { strapi });\n const releaseAction = await releaseService.createAction(releaseId, releaseActionArgs);\n\n ctx.body = {\n data: releaseAction,\n };\n },\n\n async findMany(ctx: Koa.Context) {\n const releaseId: GetReleaseActions.Request['params']['releaseId'] = ctx.params.releaseId;\n const permissionsManager = strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: RELEASE_ACTION_MODEL_UID,\n });\n const query = await permissionsManager.sanitizeQuery(ctx.query);\n\n const releaseService = getService('release', { strapi });\n const { results, pagination } = await releaseService.findActions(releaseId, {\n sort: query.groupBy === 'action' ? 'type' : query.groupBy,\n ...query,\n });\n\n /**\n * Release actions can be related to entries of different content types.\n * We need to sanitize the entry output according to that content type.\n * So, we group the sanitized output function by content type.\n */\n const contentTypeOutputSanitizers = results.reduce((acc, action) => {\n if (acc[action.contentType]) {\n return acc;\n }\n\n const contentTypePermissionsManager =\n strapi.admin.services.permission.createPermissionsManager({\n ability: ctx.state.userAbility,\n model: action.contentType,\n });\n\n acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;\n\n return acc;\n }, {});\n\n /**\n * sanitizeOutput doesn't work if you use it directly on the Release Action model, it doesn't sanitize the entries\n * So, we need to sanitize manually each entry inside a Release Action\n */\n const sanitizedResults = await mapAsync(results, async (action) => ({\n ...action,\n entry: await contentTypeOutputSanitizers[action.contentType](action.entry),\n }));\n\n const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);\n\n const contentTypes = releaseService.getContentTypeModelsFromActions(results);\n const components = await releaseService.getAllComponents();\n\n ctx.body = {\n data: groupedData,\n meta: {\n pagination,\n contentTypes,\n components,\n },\n };\n },\n\n async update(ctx: Koa.Context) {\n const actionId: UpdateReleaseAction.Request['params']['actionId'] = ctx.params.actionId;\n const releaseId: UpdateReleaseAction.Request['params']['releaseId'] = ctx.params.releaseId;\n const releaseActionUpdateArgs: UpdateReleaseAction.Request['body'] = ctx.request.body;\n\n await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);\n\n const releaseService = getService('release', { strapi });\n\n const updatedAction = await releaseService.updateAction(\n actionId,\n releaseId,\n releaseActionUpdateArgs\n );\n\n ctx.body = {\n data: updatedAction,\n };\n },\n\n async delete(ctx: Koa.Context) {\n const actionId: DeleteReleaseAction.Request['params']['actionId'] = ctx.params.actionId;\n const releaseId: DeleteReleaseAction.Request['params']['releaseId'] = ctx.params.releaseId;\n\n const releaseService = getService('release', { strapi });\n\n const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);\n\n ctx.body = {\n data: deletedReleaseAction,\n };\n },\n};\n\nexport default releaseActionController;\n","import release from './release';\nimport releaseAction from './release-action';\n\nexport const controllers = { release, 'release-action': releaseAction };\n","export default {\n type: 'admin',\n routes: [\n {\n method: 'POST',\n path: '/',\n handler: 'release.create',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.create'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/',\n handler: 'release.findMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.read'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/:id',\n handler: 'release.findOne',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.read'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/:id',\n handler: 'release.update',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.update'],\n },\n },\n ],\n },\n },\n {\n method: 'DELETE',\n path: '/:id',\n handler: 'release.delete',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.delete'],\n },\n },\n ],\n },\n },\n {\n method: 'POST',\n path: '/:id/publish',\n handler: 'release.publish',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.publish'],\n },\n },\n ],\n },\n },\n ],\n};\n","export default {\n type: 'admin',\n routes: [\n {\n method: 'POST',\n path: '/:releaseId/actions',\n handler: 'release-action.create',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.create-action'],\n },\n },\n ],\n },\n },\n {\n method: 'GET',\n path: '/:releaseId/actions',\n handler: 'release-action.findMany',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.read'],\n },\n },\n ],\n },\n },\n {\n method: 'PUT',\n path: '/:releaseId/actions/:actionId',\n handler: 'release-action.update',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.update'],\n },\n },\n ],\n },\n },\n {\n method: 'DELETE',\n path: '/:releaseId/actions/:actionId',\n handler: 'release-action.delete',\n config: {\n policies: [\n 'admin::isAuthenticatedAdmin',\n {\n name: 'admin::hasPermissions',\n config: {\n actions: ['plugin::content-releases.delete-action'],\n },\n },\n ],\n },\n },\n ],\n};\n","import release from './release';\nimport releaseAction from './release-action';\n\nexport const routes = {\n release,\n 'release-action': releaseAction,\n};\n","/* eslint-disable @typescript-eslint/no-var-requires */\nimport { register } from './register';\nimport { bootstrap } from './bootstrap';\nimport { destroy } from './destroy';\nimport { contentTypes } from './content-types';\nimport { services } from './services';\nimport { controllers } from './controllers';\nimport { routes } from './routes';\n\nconst { features } = require('@strapi/strapi/dist/utils/ee');\n\nconst getPlugin = () => {\n if (features.isEnabled('cms-content-releases')) {\n return {\n register,\n bootstrap,\n destroy,\n contentTypes,\n services,\n controllers,\n routes,\n };\n }\n\n // We keep returning contentTypes to avoid lost the data if feature is disabled\n return {\n contentTypes,\n };\n};\n\nexport default getPlugin();\n"],"names":["contentTypes","contentTypesUtils","difference","keys","mapAsync","features","strapi","release","schema","releaseAction","_","setCreatorFields","errors","EE","scheduleJob","releaseValidation","scheduling","yup","validateYupSchema"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { contentTypes as contentTypes$1, mapAsync, setCreatorFields, errors, val
2
2
  import { difference, keys } from "lodash";
3
3
  import _ from "lodash/fp";
4
4
  import EE from "@strapi/strapi/dist/utils/ee";
5
+ import { scheduleJob } from "node-schedule";
5
6
  import * as yup from "yup";
6
7
  const RELEASE_MODEL_UID = "plugin::content-releases.release";
7
8
  const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
@@ -83,6 +84,9 @@ const register = async ({ strapi: strapi2 }) => {
83
84
  strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
84
85
  }
85
86
  };
87
+ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
88
+ return strapi2.plugin("content-releases").service(name);
89
+ };
86
90
  const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
87
91
  const bootstrap = async ({ strapi: strapi2 }) => {
88
92
  if (features$1.isEnabled("cms-content-releases")) {
@@ -130,6 +134,24 @@ const bootstrap = async ({ strapi: strapi2 }) => {
130
134
  }
131
135
  }
132
136
  });
137
+ if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
138
+ getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
139
+ strapi2.log.error(
140
+ "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
141
+ );
142
+ throw err;
143
+ });
144
+ }
145
+ }
146
+ };
147
+ const destroy = async ({ strapi: strapi2 }) => {
148
+ if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
149
+ const scheduledJobs = getService("scheduling", {
150
+ strapi: strapi2
151
+ }).getAll();
152
+ for (const [, job] of scheduledJobs) {
153
+ job.cancel();
154
+ }
133
155
  }
134
156
  };
135
157
  const schema$1 = {
@@ -158,6 +180,12 @@ const schema$1 = {
158
180
  releasedAt: {
159
181
  type: "datetime"
160
182
  },
183
+ scheduledAt: {
184
+ type: "datetime"
185
+ },
186
+ timezone: {
187
+ type: "string"
188
+ },
161
189
  actions: {
162
190
  type: "relation",
163
191
  relation: "oneToMany",
@@ -220,9 +248,6 @@ const contentTypes = {
220
248
  release: release$1,
221
249
  "release-action": releaseAction$1
222
250
  };
223
- const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
224
- return strapi2.plugin("content-releases").service(name);
225
- };
226
251
  const getGroupName = (queryValue) => {
227
252
  switch (queryValue) {
228
253
  case "contentType":
@@ -238,17 +263,24 @@ const getGroupName = (queryValue) => {
238
263
  const createReleaseService = ({ strapi: strapi2 }) => ({
239
264
  async create(releaseData, { user }) {
240
265
  const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
241
- const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease } = getService(
242
- "release-validation",
243
- { strapi: strapi2 }
244
- );
266
+ const {
267
+ validatePendingReleasesLimit,
268
+ validateUniqueNameForPendingRelease,
269
+ validateScheduledAtIsLaterThanNow
270
+ } = getService("release-validation", { strapi: strapi2 });
245
271
  await Promise.all([
246
272
  validatePendingReleasesLimit(),
247
- validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
273
+ validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
274
+ validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
248
275
  ]);
249
- return strapi2.entityService.create(RELEASE_MODEL_UID, {
276
+ const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
250
277
  data: releaseWithCreatorFields
251
278
  });
279
+ if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
280
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
281
+ await schedulingService.set(release2.id, release2.scheduledAt);
282
+ }
283
+ return release2;
252
284
  },
253
285
  async findOne(id, query = {}) {
254
286
  const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
@@ -343,6 +375,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
343
375
  },
344
376
  async update(id, releaseData, { user }) {
345
377
  const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(releaseData);
378
+ const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
379
+ "release-validation",
380
+ { strapi: strapi2 }
381
+ );
382
+ await Promise.all([
383
+ validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
384
+ validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
385
+ ]);
346
386
  const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
347
387
  if (!release2) {
348
388
  throw new errors.NotFoundError(`No release found for id ${id}`);
@@ -358,6 +398,14 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
358
398
  // @ts-expect-error see above
359
399
  data: releaseWithCreatorFields
360
400
  });
401
+ if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
402
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
403
+ if (releaseData.scheduledAt) {
404
+ await schedulingService.set(id, releaseData.scheduledAt);
405
+ } else if (release2.scheduledAt) {
406
+ schedulingService.cancel(id);
407
+ }
408
+ }
361
409
  return updatedRelease;
362
410
  },
363
411
  async createAction(releaseId, action) {
@@ -714,27 +762,93 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
714
762
  throw new errors.ValidationError("You have reached the maximum number of pending releases");
715
763
  }
716
764
  },
717
- async validateUniqueNameForPendingRelease(name) {
765
+ async validateUniqueNameForPendingRelease(name, id) {
718
766
  const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
719
767
  filters: {
720
768
  releasedAt: {
721
769
  $null: true
722
770
  },
723
- name
771
+ name,
772
+ ...id && { id: { $ne: id } }
724
773
  }
725
774
  });
726
775
  const isNameUnique = pendingReleases.length === 0;
727
776
  if (!isNameUnique) {
728
777
  throw new errors.ValidationError(`Release with name ${name} already exists`);
729
778
  }
779
+ },
780
+ async validateScheduledAtIsLaterThanNow(scheduledAt) {
781
+ if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
782
+ throw new errors.ValidationError("Scheduled at must be later than now");
783
+ }
730
784
  }
731
785
  });
786
+ const createSchedulingService = ({ strapi: strapi2 }) => {
787
+ const scheduledJobs = /* @__PURE__ */ new Map();
788
+ return {
789
+ async set(releaseId, scheduleDate) {
790
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
791
+ if (!release2) {
792
+ throw new errors.NotFoundError(`No release found for id ${releaseId}`);
793
+ }
794
+ const job = scheduleJob(scheduleDate, async () => {
795
+ try {
796
+ await getService("release").publish(releaseId);
797
+ } catch (error) {
798
+ }
799
+ this.cancel(releaseId);
800
+ });
801
+ if (scheduledJobs.has(releaseId)) {
802
+ this.cancel(releaseId);
803
+ }
804
+ scheduledJobs.set(releaseId, job);
805
+ return scheduledJobs;
806
+ },
807
+ cancel(releaseId) {
808
+ if (scheduledJobs.has(releaseId)) {
809
+ scheduledJobs.get(releaseId).cancel();
810
+ scheduledJobs.delete(releaseId);
811
+ }
812
+ return scheduledJobs;
813
+ },
814
+ getAll() {
815
+ return scheduledJobs;
816
+ },
817
+ /**
818
+ * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
819
+ * This is useful in case the server was restarted and the scheduled jobs were lost
820
+ * This also could be used to sync different Strapi instances in case of a cluster
821
+ */
822
+ async syncFromDatabase() {
823
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
824
+ where: {
825
+ scheduledAt: {
826
+ $gte: /* @__PURE__ */ new Date()
827
+ },
828
+ releasedAt: null
829
+ }
830
+ });
831
+ for (const release2 of releases) {
832
+ this.set(release2.id, release2.scheduledAt);
833
+ }
834
+ return scheduledJobs;
835
+ }
836
+ };
837
+ };
732
838
  const services = {
733
839
  release: createReleaseService,
734
- "release-validation": createReleaseValidationService
840
+ "release-validation": createReleaseValidationService,
841
+ ...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
735
842
  };
736
843
  const RELEASE_SCHEMA = yup.object().shape({
737
- name: yup.string().trim().required()
844
+ name: yup.string().trim().required(),
845
+ // scheduledAt is a date, but we always receive strings from the client
846
+ scheduledAt: yup.string().nullable(),
847
+ timezone: yup.string().when("scheduledAt", {
848
+ is: (scheduledAt) => !!scheduledAt,
849
+ then: yup.string().required(),
850
+ otherwise: yup.string().nullable()
851
+ })
738
852
  }).required().noUnknown();
739
853
  const validateRelease = validateYupSchema(RELEASE_SCHEMA);
740
854
  const releaseController = {
@@ -1130,6 +1244,7 @@ const getPlugin = () => {
1130
1244
  return {
1131
1245
  register,
1132
1246
  bootstrap,
1247
+ destroy,
1133
1248
  contentTypes,
1134
1249
  services,
1135
1250
  controllers,