@strapi/content-releases 0.0.0-experimental.d8a676a242377cee820b59b21a05d47290d9ac73 → 0.0.0-experimental.d954d57341a6623992a0d211daaec8e245c3517d

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 (110) hide show
  1. package/dist/_chunks/{App-p8aKBitd.js → App-CqbuK4M6.js} +426 -427
  2. package/dist/_chunks/App-CqbuK4M6.js.map +1 -0
  3. package/dist/_chunks/{App-bpzO2Ljh.mjs → App-Do-Rnv0A.mjs} +406 -406
  4. package/dist/_chunks/App-Do-Rnv0A.mjs.map +1 -0
  5. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-Be3acS2L.js} +8 -7
  6. package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +1 -0
  7. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-_MxP6-Dt.mjs} +9 -8
  8. package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +1 -0
  9. package/dist/_chunks/{en-WuuhP6Bn.mjs → en-B9Ur3VsE.mjs} +11 -3
  10. package/dist/_chunks/en-B9Ur3VsE.mjs.map +1 -0
  11. package/dist/_chunks/{en-gcJJ5htG.js → en-DtFJ5ViE.js} +11 -3
  12. package/dist/_chunks/en-DtFJ5ViE.js.map +1 -0
  13. package/dist/_chunks/{index-AECgcaDa.mjs → index-D_pgdqQL.mjs} +244 -247
  14. package/dist/_chunks/index-D_pgdqQL.mjs.map +1 -0
  15. package/dist/_chunks/{index-fP3qoWZ4.js → index-Tedsw4GC.js} +244 -249
  16. package/dist/_chunks/index-Tedsw4GC.js.map +1 -0
  17. package/dist/admin/index.js +1 -15
  18. package/dist/admin/index.js.map +1 -1
  19. package/dist/admin/index.mjs +2 -16
  20. package/dist/admin/index.mjs.map +1 -1
  21. package/dist/admin/src/components/CMReleasesContainer.d.ts +22 -0
  22. package/dist/admin/src/components/RelativeTime.d.ts +28 -0
  23. package/dist/admin/src/components/ReleaseAction.d.ts +3 -0
  24. package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
  25. package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
  26. package/dist/admin/src/components/ReleaseListCell.d.ts +0 -0
  27. package/dist/admin/src/components/ReleaseModal.d.ts +17 -0
  28. package/dist/admin/src/constants.d.ts +58 -0
  29. package/dist/admin/src/index.d.ts +3 -0
  30. package/dist/admin/src/pages/App.d.ts +1 -0
  31. package/dist/admin/src/pages/PurchaseContentReleases.d.ts +2 -0
  32. package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +2 -0
  33. package/dist/admin/src/pages/ReleasesPage.d.ts +8 -0
  34. package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +181 -0
  35. package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +39 -0
  36. package/dist/admin/src/pluginId.d.ts +1 -0
  37. package/dist/admin/src/services/release.d.ts +105 -0
  38. package/dist/admin/src/store/hooks.d.ts +7 -0
  39. package/dist/admin/src/utils/api.d.ts +6 -0
  40. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
  41. package/dist/admin/src/utils/time.d.ts +1 -0
  42. package/dist/server/index.js +358 -237
  43. package/dist/server/index.js.map +1 -1
  44. package/dist/server/index.mjs +359 -237
  45. package/dist/server/index.mjs.map +1 -1
  46. package/dist/server/src/bootstrap.d.ts +5 -0
  47. package/dist/server/src/bootstrap.d.ts.map +1 -0
  48. package/dist/server/src/constants.d.ts +12 -0
  49. package/dist/server/src/constants.d.ts.map +1 -0
  50. package/dist/server/src/content-types/index.d.ts +99 -0
  51. package/dist/server/src/content-types/index.d.ts.map +1 -0
  52. package/dist/server/src/content-types/release/index.d.ts +48 -0
  53. package/dist/server/src/content-types/release/index.d.ts.map +1 -0
  54. package/dist/server/src/content-types/release/schema.d.ts +47 -0
  55. package/dist/server/src/content-types/release/schema.d.ts.map +1 -0
  56. package/dist/server/src/content-types/release-action/index.d.ts +50 -0
  57. package/dist/server/src/content-types/release-action/index.d.ts.map +1 -0
  58. package/dist/server/src/content-types/release-action/schema.d.ts +49 -0
  59. package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -0
  60. package/dist/server/src/controllers/index.d.ts +20 -0
  61. package/dist/server/src/controllers/index.d.ts.map +1 -0
  62. package/dist/server/src/controllers/release-action.d.ts +10 -0
  63. package/dist/server/src/controllers/release-action.d.ts.map +1 -0
  64. package/dist/server/src/controllers/release.d.ts +12 -0
  65. package/dist/server/src/controllers/release.d.ts.map +1 -0
  66. package/dist/server/src/controllers/validation/release-action.d.ts +8 -0
  67. package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -0
  68. package/dist/server/src/controllers/validation/release.d.ts +2 -0
  69. package/dist/server/src/controllers/validation/release.d.ts.map +1 -0
  70. package/dist/server/src/destroy.d.ts +5 -0
  71. package/dist/server/src/destroy.d.ts.map +1 -0
  72. package/dist/server/src/index.d.ts +2096 -0
  73. package/dist/server/src/index.d.ts.map +1 -0
  74. package/dist/server/src/migrations/index.d.ts +13 -0
  75. package/dist/server/src/migrations/index.d.ts.map +1 -0
  76. package/dist/server/src/register.d.ts +5 -0
  77. package/dist/server/src/register.d.ts.map +1 -0
  78. package/dist/server/src/routes/index.d.ts +35 -0
  79. package/dist/server/src/routes/index.d.ts.map +1 -0
  80. package/dist/server/src/routes/release-action.d.ts +18 -0
  81. package/dist/server/src/routes/release-action.d.ts.map +1 -0
  82. package/dist/server/src/routes/release.d.ts +18 -0
  83. package/dist/server/src/routes/release.d.ts.map +1 -0
  84. package/dist/server/src/services/index.d.ts +1826 -0
  85. package/dist/server/src/services/index.d.ts.map +1 -0
  86. package/dist/server/src/services/release.d.ts +66 -0
  87. package/dist/server/src/services/release.d.ts.map +1 -0
  88. package/dist/server/src/services/scheduling.d.ts +18 -0
  89. package/dist/server/src/services/scheduling.d.ts.map +1 -0
  90. package/dist/server/src/services/validation.d.ts +18 -0
  91. package/dist/server/src/services/validation.d.ts.map +1 -0
  92. package/dist/server/src/utils/index.d.ts +14 -0
  93. package/dist/server/src/utils/index.d.ts.map +1 -0
  94. package/dist/shared/contracts/release-actions.d.ts +131 -0
  95. package/dist/shared/contracts/release-actions.d.ts.map +1 -0
  96. package/dist/shared/contracts/releases.d.ts +182 -0
  97. package/dist/shared/contracts/releases.d.ts.map +1 -0
  98. package/dist/shared/types.d.ts +24 -0
  99. package/dist/shared/types.d.ts.map +1 -0
  100. package/dist/shared/validation-schemas.d.ts +2 -0
  101. package/dist/shared/validation-schemas.d.ts.map +1 -0
  102. package/package.json +29 -36
  103. package/dist/_chunks/App-bpzO2Ljh.mjs.map +0 -1
  104. package/dist/_chunks/App-p8aKBitd.js.map +0 -1
  105. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
  106. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
  107. package/dist/_chunks/en-WuuhP6Bn.mjs.map +0 -1
  108. package/dist/_chunks/en-gcJJ5htG.js.map +0 -1
  109. package/dist/_chunks/index-AECgcaDa.mjs.map +0 -1
  110. package/dist/_chunks/index-fP3qoWZ4.js.map +0 -1
@@ -1,8 +1,7 @@
1
- import { contentTypes as contentTypes$1, mapAsync, setCreatorFields, errors, validateYupSchema, yup as yup$1 } from "@strapi/utils";
1
+ import { contentTypes as contentTypes$1, async, setCreatorFields, errors, validateYupSchema, yup as yup$1 } from "@strapi/utils";
2
2
  import isEqual from "lodash/isEqual";
3
3
  import { difference, keys } from "lodash";
4
4
  import _ from "lodash/fp";
5
- import EE from "@strapi/strapi/dist/utils/ee";
6
5
  import { scheduleJob } from "node-schedule";
7
6
  import * as yup from "yup";
8
7
  const RELEASE_MODEL_UID = "plugin::content-releases.release";
@@ -54,16 +53,19 @@ const ACTIONS = [
54
53
  const ALLOWED_WEBHOOK_EVENTS = {
55
54
  RELEASES_PUBLISH: "releases.publish"
56
55
  };
57
- const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
56
+ const getService = (name, { strapi: strapi2 }) => {
58
57
  return strapi2.plugin("content-releases").service(name);
59
58
  };
60
- const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
59
+ const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 }) => {
61
60
  const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
62
61
  const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
63
- const entry = await strapi2.entityService.findOne(contentTypeUid, entryId, { populate });
62
+ const entry = await strapi2.db.query(contentTypeUid).findOne({
63
+ where: { id: entryId },
64
+ populate
65
+ });
64
66
  return entry;
65
67
  };
66
- const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
68
+ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 }) => {
67
69
  try {
68
70
  await strapi2.entityValidator.validateEntityCreation(
69
71
  strapi2.getModel(contentTypeUid),
@@ -98,7 +100,7 @@ async function deleteActionsOnDisableDraftAndPublish({
98
100
  async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
99
101
  const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes2)) ?? [];
100
102
  if (deletedContentTypes.length) {
101
- await mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
103
+ await async.map(deletedContentTypes, async (deletedContentTypeUID) => {
102
104
  return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
103
105
  });
104
106
  }
@@ -117,7 +119,7 @@ async function migrateIsValidAndStatusReleases() {
117
119
  }
118
120
  }
119
121
  });
120
- mapAsync(releasesWithoutStatus, async (release2) => {
122
+ async.map(releasesWithoutStatus, async (release2) => {
121
123
  const actions = release2.actions;
122
124
  const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
123
125
  for (const action of notValidatedActions) {
@@ -148,7 +150,7 @@ async function migrateIsValidAndStatusReleases() {
148
150
  }
149
151
  }
150
152
  });
151
- mapAsync(publishedReleases, async (release2) => {
153
+ async.map(publishedReleases, async (release2) => {
152
154
  return strapi.db.query(RELEASE_MODEL_UID).update({
153
155
  where: {
154
156
  id: release2.id
@@ -165,7 +167,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
165
167
  (uid) => oldContentTypes[uid]?.options?.draftAndPublish
166
168
  );
167
169
  const releasesAffected = /* @__PURE__ */ new Set();
168
- mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
170
+ async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
169
171
  const oldContentType = oldContentTypes[contentTypeUID];
170
172
  const contentType = contentTypes2[contentTypeUID];
171
173
  if (!isEqual(oldContentType?.attributes, contentType?.attributes)) {
@@ -178,7 +180,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
178
180
  release: true
179
181
  }
180
182
  });
181
- await mapAsync(actions, async (action) => {
183
+ await async.map(actions, async (action) => {
182
184
  if (action.entry && action.release) {
183
185
  const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
184
186
  strapi
@@ -201,7 +203,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
201
203
  });
202
204
  }
203
205
  }).then(() => {
204
- mapAsync(releasesAffected, async (releaseId) => {
206
+ async.map(releasesAffected, async (releaseId) => {
205
207
  return getService("release", { strapi }).updateReleaseStatus(releaseId);
206
208
  });
207
209
  });
@@ -211,13 +213,16 @@ async function disableContentTypeLocalized({ oldContentTypes, contentTypes: cont
211
213
  if (!oldContentTypes) {
212
214
  return;
213
215
  }
216
+ const i18nPlugin = strapi.plugin("i18n");
217
+ if (!i18nPlugin) {
218
+ return;
219
+ }
214
220
  for (const uid in contentTypes2) {
215
221
  if (!oldContentTypes[uid]) {
216
222
  continue;
217
223
  }
218
224
  const oldContentType = oldContentTypes[uid];
219
225
  const contentType = contentTypes2[uid];
220
- const i18nPlugin = strapi.plugin("i18n");
221
226
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
222
227
  if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
223
228
  await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
@@ -230,13 +235,16 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
230
235
  if (!oldContentTypes) {
231
236
  return;
232
237
  }
238
+ const i18nPlugin = strapi.plugin("i18n");
239
+ if (!i18nPlugin) {
240
+ return;
241
+ }
233
242
  for (const uid in contentTypes2) {
234
243
  if (!oldContentTypes[uid]) {
235
244
  continue;
236
245
  }
237
246
  const oldContentType = oldContentTypes[uid];
238
247
  const contentType = contentTypes2[uid];
239
- const i18nPlugin = strapi.plugin("i18n");
240
248
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
241
249
  const { getDefaultLocale } = i18nPlugin.service("locales");
242
250
  if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
@@ -247,11 +255,10 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
247
255
  }
248
256
  }
249
257
  }
250
- const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
251
258
  const register = async ({ strapi: strapi2 }) => {
252
- if (features$2.isEnabled("cms-content-releases")) {
253
- await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
254
- strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish).register(disableContentTypeLocalized);
259
+ if (strapi2.ee.features.isEnabled("cms-content-releases")) {
260
+ await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
261
+ strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
255
262
  strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
256
263
  }
257
264
  if (strapi2.plugin("graphql")) {
@@ -260,9 +267,8 @@ const register = async ({ strapi: strapi2 }) => {
260
267
  graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
261
268
  }
262
269
  };
263
- const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
264
270
  const bootstrap = async ({ strapi: strapi2 }) => {
265
- if (features$1.isEnabled("cms-content-releases")) {
271
+ if (strapi2.ee.features.isEnabled("cms-content-releases")) {
266
272
  const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
267
273
  (uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
268
274
  );
@@ -321,9 +327,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
321
327
  actions: {
322
328
  target_type: model.uid,
323
329
  target_id: {
324
- $in: entriesToDelete.map(
325
- (entry) => entry.id
326
- )
330
+ $in: entriesToDelete.map((entry) => entry.id)
327
331
  }
328
332
  }
329
333
  }
@@ -350,13 +354,9 @@ const bootstrap = async ({ strapi: strapi2 }) => {
350
354
  try {
351
355
  const { model, result } = event;
352
356
  if (model.kind === "collectionType" && model.options?.draftAndPublish) {
353
- const isEntryValid = await getEntryValidStatus(
354
- model.uid,
355
- result,
356
- {
357
- strapi: strapi2
358
- }
359
- );
357
+ const isEntryValid = await getEntryValidStatus(model.uid, result, {
358
+ strapi: strapi2
359
+ });
360
360
  await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
361
361
  where: {
362
362
  target_type: model.uid,
@@ -383,27 +383,23 @@ const bootstrap = async ({ strapi: strapi2 }) => {
383
383
  }
384
384
  }
385
385
  });
386
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
387
- getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
388
- strapi2.log.error(
389
- "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
390
- );
391
- throw err;
392
- });
393
- Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
394
- strapi2.webhookStore.addAllowedEvent(key, value);
395
- });
396
- }
386
+ getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
387
+ strapi2.log.error(
388
+ "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
389
+ );
390
+ throw err;
391
+ });
392
+ Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
393
+ strapi2.get("webhookStore").addAllowedEvent(key, value);
394
+ });
397
395
  }
398
396
  };
399
397
  const destroy = async ({ strapi: strapi2 }) => {
400
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
401
- const scheduledJobs = getService("scheduling", {
402
- strapi: strapi2
403
- }).getAll();
404
- for (const [, job] of scheduledJobs) {
405
- job.cancel();
406
- }
398
+ const scheduledJobs = getService("scheduling", {
399
+ strapi: strapi2
400
+ }).getAll();
401
+ for (const [, job] of scheduledJobs) {
402
+ job.cancel();
407
403
  }
408
404
  };
409
405
  const schema$1 = {
@@ -528,6 +524,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
528
524
  release: release2
529
525
  });
530
526
  };
527
+ const publishSingleTypeAction = async (uid, actionType, entryId) => {
528
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
529
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
530
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
531
+ const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
532
+ try {
533
+ if (actionType === "publish") {
534
+ await entityManagerService.publish(entry, uid);
535
+ } else {
536
+ await entityManagerService.unpublish(entry, uid);
537
+ }
538
+ } catch (error) {
539
+ if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
540
+ ;
541
+ else {
542
+ throw error;
543
+ }
544
+ }
545
+ };
546
+ const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
547
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
548
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
549
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
550
+ const entriesToPublish = await strapi2.entityService.findMany(uid, {
551
+ filters: {
552
+ id: {
553
+ $in: entriesToPublishIds
554
+ }
555
+ },
556
+ populate
557
+ });
558
+ const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
559
+ filters: {
560
+ id: {
561
+ $in: entriestoUnpublishIds
562
+ }
563
+ },
564
+ populate
565
+ });
566
+ if (entriesToPublish.length > 0) {
567
+ await entityManagerService.publishMany(entriesToPublish, uid);
568
+ }
569
+ if (entriesToUnpublish.length > 0) {
570
+ await entityManagerService.unpublishMany(entriesToUnpublish, uid);
571
+ }
572
+ };
573
+ const getFormattedActions = async (releaseId) => {
574
+ const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
575
+ where: {
576
+ release: {
577
+ id: releaseId
578
+ }
579
+ },
580
+ populate: {
581
+ entry: {
582
+ fields: ["id"]
583
+ }
584
+ }
585
+ });
586
+ if (actions.length === 0) {
587
+ throw new errors.ValidationError("No entries to publish");
588
+ }
589
+ const collectionTypeActions = {};
590
+ const singleTypeActions = [];
591
+ for (const action of actions) {
592
+ const contentTypeUid = action.contentType;
593
+ if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
594
+ if (!collectionTypeActions[contentTypeUid]) {
595
+ collectionTypeActions[contentTypeUid] = {
596
+ entriesToPublishIds: [],
597
+ entriesToUnpublishIds: []
598
+ };
599
+ }
600
+ if (action.type === "publish") {
601
+ collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
602
+ } else {
603
+ collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
604
+ }
605
+ } else {
606
+ singleTypeActions.push({
607
+ uid: contentTypeUid,
608
+ action: action.type,
609
+ id: action.entry.id
610
+ });
611
+ }
612
+ }
613
+ return { collectionTypeActions, singleTypeActions };
614
+ };
531
615
  return {
532
616
  async create(releaseData, { user }) {
533
617
  const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
@@ -541,13 +625,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
541
625
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
542
626
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
543
627
  ]);
544
- const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
628
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
545
629
  data: {
546
630
  ...releaseWithCreatorFields,
547
631
  status: "empty"
548
632
  }
549
633
  });
550
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
634
+ if (releaseWithCreatorFields.scheduledAt) {
551
635
  const schedulingService = getService("scheduling", { strapi: strapi2 });
552
636
  await schedulingService.set(release2.id, release2.scheduledAt);
553
637
  }
@@ -555,28 +639,36 @@ const createReleaseService = ({ strapi: strapi2 }) => {
555
639
  return release2;
556
640
  },
557
641
  async findOne(id, query = {}) {
558
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
559
- ...query
642
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
643
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
644
+ ...dbQuery,
645
+ where: { id }
560
646
  });
561
647
  return release2;
562
648
  },
563
649
  findPage(query) {
564
- return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
565
- ...query,
650
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
651
+ return strapi2.db.query(RELEASE_MODEL_UID).findPage({
652
+ ...dbQuery,
566
653
  populate: {
567
654
  actions: {
568
- // @ts-expect-error Ignore missing properties
569
655
  count: true
570
656
  }
571
657
  }
572
658
  });
573
659
  },
574
- async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
660
+ async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
661
+ let entries = entriesIds;
662
+ if (!Array.isArray(entriesIds)) {
663
+ entries = [entriesIds];
664
+ }
575
665
  const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
576
666
  where: {
577
667
  actions: {
578
668
  target_type: contentTypeUid,
579
- target_id: entryId
669
+ target_id: {
670
+ $in: entries
671
+ }
580
672
  },
581
673
  releasedAt: {
582
674
  $null: true
@@ -587,18 +679,25 @@ const createReleaseService = ({ strapi: strapi2 }) => {
587
679
  actions: {
588
680
  where: {
589
681
  target_type: contentTypeUid,
590
- target_id: entryId
682
+ target_id: {
683
+ $in: entries
684
+ }
685
+ },
686
+ populate: {
687
+ entry: {
688
+ select: ["id"]
689
+ }
591
690
  }
592
691
  }
593
692
  }
594
693
  });
595
694
  return releases.map((release2) => {
596
695
  if (release2.actions?.length) {
597
- const [actionForEntry] = release2.actions;
696
+ const actionsForEntry = release2.actions;
598
697
  delete release2.actions;
599
698
  return {
600
699
  ...release2,
601
- action: actionForEntry
700
+ actions: actionsForEntry
602
701
  };
603
702
  }
604
703
  return release2;
@@ -657,28 +756,22 @@ const createReleaseService = ({ strapi: strapi2 }) => {
657
756
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
658
757
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
659
758
  ]);
660
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
759
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
661
760
  if (!release2) {
662
761
  throw new errors.NotFoundError(`No release found for id ${id}`);
663
762
  }
664
763
  if (release2.releasedAt) {
665
764
  throw new errors.ValidationError("Release already published");
666
765
  }
667
- const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
668
- /*
669
- * The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
670
- * is not compatible with the type we are passing here: UpdateRelease.Request['body']
671
- */
672
- // @ts-expect-error see above
766
+ const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
767
+ where: { id },
673
768
  data: releaseWithCreatorFields
674
769
  });
675
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
676
- const schedulingService = getService("scheduling", { strapi: strapi2 });
677
- if (releaseData.scheduledAt) {
678
- await schedulingService.set(id, releaseData.scheduledAt);
679
- } else if (release2.scheduledAt) {
680
- schedulingService.cancel(id);
681
- }
770
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
771
+ if (releaseData.scheduledAt) {
772
+ await schedulingService.set(id, releaseData.scheduledAt);
773
+ } else if (release2.scheduledAt) {
774
+ schedulingService.cancel(id);
682
775
  }
683
776
  this.updateReleaseStatus(id);
684
777
  strapi2.telemetry.send("didUpdateContentRelease");
@@ -692,7 +785,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
692
785
  validateEntryContentType(action.entry.contentType),
693
786
  validateUniqueEntry(releaseId, action)
694
787
  ]);
695
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
788
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
696
789
  if (!release2) {
697
790
  throw new errors.NotFoundError(`No release found for id ${releaseId}`);
698
791
  }
@@ -702,7 +795,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
702
795
  const { entry, type } = action;
703
796
  const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
704
797
  const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
705
- const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
798
+ const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
706
799
  data: {
707
800
  type,
708
801
  contentType: entry.contentType,
@@ -715,32 +808,35 @@ const createReleaseService = ({ strapi: strapi2 }) => {
715
808
  },
716
809
  release: releaseId
717
810
  },
718
- populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
811
+ populate: { release: { select: ["id"] }, entry: { select: ["id"] } }
719
812
  });
720
813
  this.updateReleaseStatus(releaseId);
721
814
  return releaseAction2;
722
815
  },
723
816
  async findActions(releaseId, query) {
724
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
725
- fields: ["id"]
817
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
818
+ where: { id: releaseId },
819
+ select: ["id"]
726
820
  });
727
821
  if (!release2) {
728
822
  throw new errors.NotFoundError(`No release found for id ${releaseId}`);
729
823
  }
730
- return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
731
- ...query,
824
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
825
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
826
+ ...dbQuery,
732
827
  populate: {
733
828
  entry: {
734
829
  populate: "*"
735
830
  }
736
831
  },
737
- filters: {
832
+ where: {
738
833
  release: releaseId
739
834
  }
740
835
  });
741
836
  },
742
837
  async countActions(query) {
743
- return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
838
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
839
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
744
840
  },
745
841
  async groupActions(actions, groupBy) {
746
842
  const contentTypeUids = actions.reduce((acc, action) => {
@@ -749,9 +845,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
749
845
  }
750
846
  return acc;
751
847
  }, []);
752
- const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
753
- contentTypeUids
754
- );
848
+ const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(contentTypeUids);
755
849
  const allLocalesDictionary = await this.getLocalesDataForActions();
756
850
  const formattedData = actions.map((action) => {
757
851
  const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
@@ -821,10 +915,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
821
915
  return componentsMap;
822
916
  },
823
917
  async delete(releaseId) {
824
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
918
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
919
+ where: { id: releaseId },
825
920
  populate: {
826
921
  actions: {
827
- fields: ["id"]
922
+ select: ["id"]
828
923
  }
829
924
  }
830
925
  });
@@ -842,9 +937,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
842
937
  }
843
938
  }
844
939
  });
845
- await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
940
+ await strapi2.db.query(RELEASE_MODEL_UID).delete({
941
+ where: {
942
+ id: releaseId
943
+ }
944
+ });
846
945
  });
847
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
946
+ if (release2.scheduledAt) {
848
947
  const schedulingService = getService("scheduling", { strapi: strapi2 });
849
948
  await schedulingService.cancel(release2.id);
850
949
  }
@@ -852,145 +951,69 @@ const createReleaseService = ({ strapi: strapi2 }) => {
852
951
  return release2;
853
952
  },
854
953
  async publish(releaseId) {
855
- try {
856
- const releaseWithPopulatedActionEntries = await strapi2.entityService.findOne(
857
- RELEASE_MODEL_UID,
858
- releaseId,
859
- {
860
- populate: {
861
- actions: {
862
- populate: {
863
- entry: {
864
- fields: ["id"]
865
- }
866
- }
867
- }
868
- }
869
- }
870
- );
871
- if (!releaseWithPopulatedActionEntries) {
954
+ const {
955
+ release: release2,
956
+ error
957
+ } = await strapi2.db.transaction(async ({ trx }) => {
958
+ const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
959
+ if (!lockedRelease) {
872
960
  throw new errors.NotFoundError(`No release found for id ${releaseId}`);
873
961
  }
874
- if (releaseWithPopulatedActionEntries.releasedAt) {
962
+ if (lockedRelease.releasedAt) {
875
963
  throw new errors.ValidationError("Release already published");
876
964
  }
877
- if (releaseWithPopulatedActionEntries.actions.length === 0) {
878
- throw new errors.ValidationError("No entries to publish");
965
+ if (lockedRelease.status === "failed") {
966
+ throw new errors.ValidationError("Release failed to publish");
879
967
  }
880
- const collectionTypeActions = {};
881
- const singleTypeActions = [];
882
- for (const action of releaseWithPopulatedActionEntries.actions) {
883
- const contentTypeUid = action.contentType;
884
- if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
885
- if (!collectionTypeActions[contentTypeUid]) {
886
- collectionTypeActions[contentTypeUid] = {
887
- entriestoPublishIds: [],
888
- entriesToUnpublishIds: []
889
- };
890
- }
891
- if (action.type === "publish") {
892
- collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
893
- } else {
894
- collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
895
- }
896
- } else {
897
- singleTypeActions.push({
898
- uid: contentTypeUid,
899
- action: action.type,
900
- id: action.entry.id
901
- });
902
- }
903
- }
904
- const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
905
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
906
- await strapi2.db.transaction(async () => {
907
- for (const { uid, action, id } of singleTypeActions) {
908
- const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
909
- const entry = await strapi2.entityService.findOne(uid, id, { populate });
910
- try {
911
- if (action === "publish") {
912
- await entityManagerService.publish(entry, uid);
913
- } else {
914
- await entityManagerService.unpublish(entry, uid);
915
- }
916
- } catch (error) {
917
- if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
918
- } else {
919
- throw error;
920
- }
921
- }
922
- }
923
- for (const contentTypeUid of Object.keys(collectionTypeActions)) {
924
- const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
925
- const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
926
- const entriesToPublish = await strapi2.entityService.findMany(
927
- contentTypeUid,
928
- {
929
- filters: {
930
- id: {
931
- $in: entriestoPublishIds
932
- }
933
- },
934
- populate
935
- }
936
- );
937
- const entriesToUnpublish = await strapi2.entityService.findMany(
938
- contentTypeUid,
939
- {
940
- filters: {
941
- id: {
942
- $in: entriesToUnpublishIds
943
- }
944
- },
945
- populate
946
- }
947
- );
948
- if (entriesToPublish.length > 0) {
949
- await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
968
+ try {
969
+ strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
970
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(releaseId);
971
+ await strapi2.db.transaction(async () => {
972
+ for (const { uid, action, id } of singleTypeActions) {
973
+ await publishSingleTypeAction(uid, action, id);
950
974
  }
951
- if (entriesToUnpublish.length > 0) {
952
- await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
975
+ for (const contentTypeUid of Object.keys(collectionTypeActions)) {
976
+ const uid = contentTypeUid;
977
+ await publishCollectionTypeAction(
978
+ uid,
979
+ collectionTypeActions[uid].entriesToPublishIds,
980
+ collectionTypeActions[uid].entriesToUnpublishIds
981
+ );
953
982
  }
954
- }
955
- });
956
- const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
957
- data: {
958
- /*
959
- * The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
960
- */
961
- // @ts-expect-error see above
962
- releasedAt: /* @__PURE__ */ new Date()
963
- },
964
- populate: {
965
- actions: {
966
- // @ts-expect-error is not expecting count but it is working
967
- count: true
983
+ });
984
+ const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
985
+ where: {
986
+ id: releaseId
987
+ },
988
+ data: {
989
+ status: "done",
990
+ releasedAt: /* @__PURE__ */ new Date()
968
991
  }
969
- }
970
- });
971
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
992
+ });
972
993
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
973
994
  isPublished: true,
974
- release: release2
995
+ release: release22
975
996
  });
976
- }
977
- strapi2.telemetry.send("didPublishContentRelease");
978
- return release2;
979
- } catch (error) {
980
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
997
+ strapi2.telemetry.send("didPublishContentRelease");
998
+ return { release: release22, error: null };
999
+ } catch (error2) {
981
1000
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
982
1001
  isPublished: false,
983
- error
1002
+ error: error2
984
1003
  });
985
- }
986
- strapi2.db.query(RELEASE_MODEL_UID).update({
987
- where: { id: releaseId },
988
- data: {
1004
+ await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
989
1005
  status: "failed"
990
- }
991
- });
1006
+ }).transacting(trx).execute();
1007
+ return {
1008
+ release: null,
1009
+ error: error2
1010
+ };
1011
+ }
1012
+ });
1013
+ if (error instanceof Error) {
992
1014
  throw error;
993
1015
  }
1016
+ return release2;
994
1017
  },
995
1018
  async updateAction(actionId, releaseId, update) {
996
1019
  const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
@@ -1077,10 +1100,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1077
1100
  }
1078
1101
  };
1079
1102
  };
1103
+ class AlreadyOnReleaseError extends errors.ApplicationError {
1104
+ constructor(message) {
1105
+ super(message);
1106
+ this.name = "AlreadyOnReleaseError";
1107
+ }
1108
+ }
1080
1109
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1081
1110
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1082
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
1083
- populate: { actions: { populate: { entry: { fields: ["id"] } } } }
1111
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
1112
+ where: {
1113
+ id: releaseId
1114
+ },
1115
+ populate: { actions: { populate: { entry: { select: ["id"] } } } }
1084
1116
  });
1085
1117
  if (!release2) {
1086
1118
  throw new errors.NotFoundError(`No release found for id ${releaseId}`);
@@ -1089,7 +1121,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1089
1121
  (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1090
1122
  );
1091
1123
  if (isEntryInRelease) {
1092
- throw new errors.ValidationError(
1124
+ throw new AlreadyOnReleaseError(
1093
1125
  `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1094
1126
  );
1095
1127
  }
@@ -1106,10 +1138,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1106
1138
  }
1107
1139
  },
1108
1140
  async validatePendingReleasesLimit() {
1109
- const maximumPendingReleases = (
1110
- // @ts-expect-error - options is not typed into features
1111
- EE.features.get("cms-content-releases")?.options?.maximumReleases || 3
1112
- );
1141
+ const featureCfg = strapi2.ee.features.get("cms-content-releases");
1142
+ const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
1113
1143
  const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
1114
1144
  filters: {
1115
1145
  releasedAt: {
@@ -1122,8 +1152,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1122
1152
  }
1123
1153
  },
1124
1154
  async validateUniqueNameForPendingRelease(name, id) {
1125
- const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
1126
- filters: {
1155
+ const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
1156
+ where: {
1127
1157
  releasedAt: {
1128
1158
  $null: true
1129
1159
  },
@@ -1152,7 +1182,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1152
1182
  }
1153
1183
  const job = scheduleJob(scheduleDate, async () => {
1154
1184
  try {
1155
- await getService("release").publish(releaseId);
1185
+ await getService("release", { strapi: strapi2 }).publish(releaseId);
1156
1186
  } catch (error) {
1157
1187
  }
1158
1188
  this.cancel(releaseId);
@@ -1197,7 +1227,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1197
1227
  const services = {
1198
1228
  release: createReleaseService,
1199
1229
  "release-validation": createReleaseValidationService,
1200
- ...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
1230
+ scheduling: createSchedulingService
1201
1231
  };
1202
1232
  const RELEASE_SCHEMA = yup.object().shape({
1203
1233
  name: yup.string().trim().required(),
@@ -1222,7 +1252,7 @@ const RELEASE_SCHEMA = yup.object().shape({
1222
1252
  const validateRelease = validateYupSchema(RELEASE_SCHEMA);
1223
1253
  const releaseController = {
1224
1254
  async findMany(ctx) {
1225
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1255
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1226
1256
  ability: ctx.state.userAbility,
1227
1257
  model: RELEASE_MODEL_UID
1228
1258
  });
@@ -1250,7 +1280,7 @@ const releaseController = {
1250
1280
  }
1251
1281
  };
1252
1282
  });
1253
- const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
1283
+ const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1254
1284
  where: {
1255
1285
  releasedAt: null
1256
1286
  }
@@ -1272,7 +1302,7 @@ const releaseController = {
1272
1302
  });
1273
1303
  const sanitizedRelease = {
1274
1304
  ...release2,
1275
- createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
1305
+ createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
1276
1306
  };
1277
1307
  const data = {
1278
1308
  ...sanitizedRelease,
@@ -1284,19 +1314,48 @@ const releaseController = {
1284
1314
  };
1285
1315
  ctx.body = { data };
1286
1316
  },
1317
+ async mapEntriesToReleases(ctx) {
1318
+ const { contentTypeUid, entriesIds } = ctx.query;
1319
+ if (!contentTypeUid || !entriesIds) {
1320
+ throw new errors.ValidationError("Missing required query parameters");
1321
+ }
1322
+ const releaseService = getService("release", { strapi });
1323
+ const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1324
+ contentTypeUid,
1325
+ entriesIds
1326
+ );
1327
+ const mappedEntriesInReleases = releasesWithActions.reduce(
1328
+ // TODO: Fix for v5 removed mappedEntriedToRelease
1329
+ (acc, release2) => {
1330
+ release2.actions.forEach((action) => {
1331
+ if (!acc[action.entry.id]) {
1332
+ acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
1333
+ } else {
1334
+ acc[action.entry.id].push({ id: release2.id, name: release2.name });
1335
+ }
1336
+ });
1337
+ return acc;
1338
+ },
1339
+ // TODO: Fix for v5 removed mappedEntriedToRelease
1340
+ {}
1341
+ );
1342
+ ctx.body = {
1343
+ data: mappedEntriesInReleases
1344
+ };
1345
+ },
1287
1346
  async create(ctx) {
1288
1347
  const user = ctx.state.user;
1289
1348
  const releaseArgs = ctx.request.body;
1290
1349
  await validateRelease(releaseArgs);
1291
1350
  const releaseService = getService("release", { strapi });
1292
1351
  const release2 = await releaseService.create(releaseArgs, { user });
1293
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1352
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1294
1353
  ability: ctx.state.userAbility,
1295
1354
  model: RELEASE_MODEL_UID
1296
1355
  });
1297
- ctx.body = {
1356
+ ctx.created({
1298
1357
  data: await permissionsManager.sanitizeOutput(release2)
1299
- };
1358
+ });
1300
1359
  },
1301
1360
  async update(ctx) {
1302
1361
  const user = ctx.state.user;
@@ -1305,7 +1364,7 @@ const releaseController = {
1305
1364
  await validateRelease(releaseArgs);
1306
1365
  const releaseService = getService("release", { strapi });
1307
1366
  const release2 = await releaseService.update(id, releaseArgs, { user });
1308
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1367
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1309
1368
  ability: ctx.state.userAbility,
1310
1369
  model: RELEASE_MODEL_UID
1311
1370
  });
@@ -1369,13 +1428,45 @@ const releaseActionController = {
1369
1428
  await validateReleaseAction(releaseActionArgs);
1370
1429
  const releaseService = getService("release", { strapi });
1371
1430
  const releaseAction2 = await releaseService.createAction(releaseId, releaseActionArgs);
1372
- ctx.body = {
1431
+ ctx.created({
1373
1432
  data: releaseAction2
1374
- };
1433
+ });
1434
+ },
1435
+ async createMany(ctx) {
1436
+ const releaseId = ctx.params.releaseId;
1437
+ const releaseActionsArgs = ctx.request.body;
1438
+ await Promise.all(
1439
+ releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1440
+ );
1441
+ const releaseService = getService("release", { strapi });
1442
+ const releaseActions = await strapi.db.transaction(async () => {
1443
+ const releaseActions2 = await Promise.all(
1444
+ releaseActionsArgs.map(async (releaseActionArgs) => {
1445
+ try {
1446
+ const action = await releaseService.createAction(releaseId, releaseActionArgs);
1447
+ return action;
1448
+ } catch (error) {
1449
+ if (error instanceof AlreadyOnReleaseError) {
1450
+ return null;
1451
+ }
1452
+ throw error;
1453
+ }
1454
+ })
1455
+ );
1456
+ return releaseActions2;
1457
+ });
1458
+ const newReleaseActions = releaseActions.filter((action) => action !== null);
1459
+ ctx.created({
1460
+ data: newReleaseActions,
1461
+ meta: {
1462
+ entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1463
+ totalEntries: releaseActions.length
1464
+ }
1465
+ });
1375
1466
  },
1376
1467
  async findMany(ctx) {
1377
1468
  const releaseId = ctx.params.releaseId;
1378
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1469
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1379
1470
  ability: ctx.state.userAbility,
1380
1471
  model: RELEASE_ACTION_MODEL_UID
1381
1472
  });
@@ -1389,14 +1480,14 @@ const releaseActionController = {
1389
1480
  if (acc[action.contentType]) {
1390
1481
  return acc;
1391
1482
  }
1392
- const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
1483
+ const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
1393
1484
  ability: ctx.state.userAbility,
1394
1485
  model: action.contentType
1395
1486
  });
1396
1487
  acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
1397
1488
  return acc;
1398
1489
  }, {});
1399
- const sanitizedResults = await mapAsync(results, async (action) => ({
1490
+ const sanitizedResults = await async.map(results, async (action) => ({
1400
1491
  ...action,
1401
1492
  entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
1402
1493
  }));
@@ -1441,6 +1532,22 @@ const controllers = { release: releaseController, "release-action": releaseActio
1441
1532
  const release = {
1442
1533
  type: "admin",
1443
1534
  routes: [
1535
+ {
1536
+ method: "GET",
1537
+ path: "/mapEntriesToReleases",
1538
+ handler: "release.mapEntriesToReleases",
1539
+ config: {
1540
+ policies: [
1541
+ "admin::isAuthenticatedAdmin",
1542
+ {
1543
+ name: "admin::hasPermissions",
1544
+ config: {
1545
+ actions: ["plugin::content-releases.read"]
1546
+ }
1547
+ }
1548
+ ]
1549
+ }
1550
+ },
1444
1551
  {
1445
1552
  method: "POST",
1446
1553
  path: "/",
@@ -1558,6 +1665,22 @@ const releaseAction = {
1558
1665
  ]
1559
1666
  }
1560
1667
  },
1668
+ {
1669
+ method: "POST",
1670
+ path: "/:releaseId/actions/bulk",
1671
+ handler: "release-action.createMany",
1672
+ config: {
1673
+ policies: [
1674
+ "admin::isAuthenticatedAdmin",
1675
+ {
1676
+ name: "admin::hasPermissions",
1677
+ config: {
1678
+ actions: ["plugin::content-releases.create-action"]
1679
+ }
1680
+ }
1681
+ ]
1682
+ }
1683
+ },
1561
1684
  {
1562
1685
  method: "GET",
1563
1686
  path: "/:releaseId/actions",
@@ -1612,9 +1735,8 @@ const routes = {
1612
1735
  release,
1613
1736
  "release-action": releaseAction
1614
1737
  };
1615
- const { features } = require("@strapi/strapi/dist/utils/ee");
1616
1738
  const getPlugin = () => {
1617
- if (features.isEnabled("cms-content-releases")) {
1739
+ if (strapi.ee.features.isEnabled("cms-content-releases")) {
1618
1740
  return {
1619
1741
  register,
1620
1742
  bootstrap,