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

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-C0DlH0im.js} +343 -323
  2. package/dist/_chunks/App-C0DlH0im.js.map +1 -0
  3. package/dist/_chunks/{App-bpzO2Ljh.mjs → App-O0ZO-S35.mjs} +328 -308
  4. package/dist/_chunks/App-O0ZO-S35.mjs.map +1 -0
  5. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-DAHdUpAA.js} +5 -5
  6. package/dist/_chunks/PurchaseContentReleases-DAHdUpAA.js.map +1 -0
  7. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-Ex09YpKR.mjs} +6 -6
  8. package/dist/_chunks/PurchaseContentReleases-Ex09YpKR.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-DjDPK8kb.mjs} +337 -109
  14. package/dist/_chunks/index-DjDPK8kb.mjs.map +1 -0
  15. package/dist/_chunks/{index-fP3qoWZ4.js → index-DoZNNtsb.js} +340 -112
  16. package/dist/_chunks/index-DoZNNtsb.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 +16 -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/axios.d.ts +29 -0
  38. package/dist/admin/src/services/release.d.ts +429 -0
  39. package/dist/admin/src/store/hooks.d.ts +7 -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 +26 -31
  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
@@ -3,7 +3,6 @@ const utils = require("@strapi/utils");
3
3
  const isEqual = require("lodash/isEqual");
4
4
  const lodash = require("lodash");
5
5
  const _ = require("lodash/fp");
6
- const EE = require("@strapi/strapi/dist/utils/ee");
7
6
  const nodeSchedule = require("node-schedule");
8
7
  const yup = require("yup");
9
8
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
@@ -27,7 +26,6 @@ function _interopNamespace(e) {
27
26
  }
28
27
  const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
29
28
  const ___default = /* @__PURE__ */ _interopDefault(_);
30
- const EE__default = /* @__PURE__ */ _interopDefault(EE);
31
29
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
32
30
  const RELEASE_MODEL_UID = "plugin::content-releases.release";
33
31
  const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
@@ -78,16 +76,19 @@ const ACTIONS = [
78
76
  const ALLOWED_WEBHOOK_EVENTS = {
79
77
  RELEASES_PUBLISH: "releases.publish"
80
78
  };
81
- const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
79
+ const getService = (name, { strapi: strapi2 }) => {
82
80
  return strapi2.plugin("content-releases").service(name);
83
81
  };
84
- const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
82
+ const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 }) => {
85
83
  const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
86
84
  const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
87
- const entry = await strapi2.entityService.findOne(contentTypeUid, entryId, { populate });
85
+ const entry = await strapi2.db.query(contentTypeUid).findOne({
86
+ where: { id: entryId },
87
+ populate
88
+ });
88
89
  return entry;
89
90
  };
90
- const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
91
+ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 }) => {
91
92
  try {
92
93
  await strapi2.entityValidator.validateEntityCreation(
93
94
  strapi2.getModel(contentTypeUid),
@@ -122,7 +123,7 @@ async function deleteActionsOnDisableDraftAndPublish({
122
123
  async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
123
124
  const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
124
125
  if (deletedContentTypes.length) {
125
- await utils.mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
126
+ await utils.async.map(deletedContentTypes, async (deletedContentTypeUID) => {
126
127
  return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
127
128
  });
128
129
  }
@@ -141,7 +142,7 @@ async function migrateIsValidAndStatusReleases() {
141
142
  }
142
143
  }
143
144
  });
144
- utils.mapAsync(releasesWithoutStatus, async (release2) => {
145
+ utils.async.map(releasesWithoutStatus, async (release2) => {
145
146
  const actions = release2.actions;
146
147
  const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
147
148
  for (const action of notValidatedActions) {
@@ -172,7 +173,7 @@ async function migrateIsValidAndStatusReleases() {
172
173
  }
173
174
  }
174
175
  });
175
- utils.mapAsync(publishedReleases, async (release2) => {
176
+ utils.async.map(publishedReleases, async (release2) => {
176
177
  return strapi.db.query(RELEASE_MODEL_UID).update({
177
178
  where: {
178
179
  id: release2.id
@@ -189,7 +190,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
189
190
  (uid) => oldContentTypes[uid]?.options?.draftAndPublish
190
191
  );
191
192
  const releasesAffected = /* @__PURE__ */ new Set();
192
- utils.mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
193
+ utils.async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
193
194
  const oldContentType = oldContentTypes[contentTypeUID];
194
195
  const contentType = contentTypes2[contentTypeUID];
195
196
  if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
@@ -202,7 +203,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
202
203
  release: true
203
204
  }
204
205
  });
205
- await utils.mapAsync(actions, async (action) => {
206
+ await utils.async.map(actions, async (action) => {
206
207
  if (action.entry && action.release) {
207
208
  const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
208
209
  strapi
@@ -225,7 +226,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
225
226
  });
226
227
  }
227
228
  }).then(() => {
228
- utils.mapAsync(releasesAffected, async (releaseId) => {
229
+ utils.async.map(releasesAffected, async (releaseId) => {
229
230
  return getService("release", { strapi }).updateReleaseStatus(releaseId);
230
231
  });
231
232
  });
@@ -235,13 +236,16 @@ async function disableContentTypeLocalized({ oldContentTypes, contentTypes: cont
235
236
  if (!oldContentTypes) {
236
237
  return;
237
238
  }
239
+ const i18nPlugin = strapi.plugin("i18n");
240
+ if (!i18nPlugin) {
241
+ return;
242
+ }
238
243
  for (const uid in contentTypes2) {
239
244
  if (!oldContentTypes[uid]) {
240
245
  continue;
241
246
  }
242
247
  const oldContentType = oldContentTypes[uid];
243
248
  const contentType = contentTypes2[uid];
244
- const i18nPlugin = strapi.plugin("i18n");
245
249
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
246
250
  if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
247
251
  await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
@@ -254,13 +258,16 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
254
258
  if (!oldContentTypes) {
255
259
  return;
256
260
  }
261
+ const i18nPlugin = strapi.plugin("i18n");
262
+ if (!i18nPlugin) {
263
+ return;
264
+ }
257
265
  for (const uid in contentTypes2) {
258
266
  if (!oldContentTypes[uid]) {
259
267
  continue;
260
268
  }
261
269
  const oldContentType = oldContentTypes[uid];
262
270
  const contentType = contentTypes2[uid];
263
- const i18nPlugin = strapi.plugin("i18n");
264
271
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
265
272
  const { getDefaultLocale } = i18nPlugin.service("locales");
266
273
  if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
@@ -271,11 +278,10 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
271
278
  }
272
279
  }
273
280
  }
274
- const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
275
281
  const register = async ({ strapi: strapi2 }) => {
276
- if (features$2.isEnabled("cms-content-releases")) {
277
- await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
278
- strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish).register(disableContentTypeLocalized);
282
+ if (strapi2.ee.features.isEnabled("cms-content-releases")) {
283
+ await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
284
+ strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
279
285
  strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
280
286
  }
281
287
  if (strapi2.plugin("graphql")) {
@@ -284,9 +290,8 @@ const register = async ({ strapi: strapi2 }) => {
284
290
  graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
285
291
  }
286
292
  };
287
- const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
288
293
  const bootstrap = async ({ strapi: strapi2 }) => {
289
- if (features$1.isEnabled("cms-content-releases")) {
294
+ if (strapi2.ee.features.isEnabled("cms-content-releases")) {
290
295
  const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
291
296
  (uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
292
297
  );
@@ -345,9 +350,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
345
350
  actions: {
346
351
  target_type: model.uid,
347
352
  target_id: {
348
- $in: entriesToDelete.map(
349
- (entry) => entry.id
350
- )
353
+ $in: entriesToDelete.map((entry) => entry.id)
351
354
  }
352
355
  }
353
356
  }
@@ -374,13 +377,9 @@ const bootstrap = async ({ strapi: strapi2 }) => {
374
377
  try {
375
378
  const { model, result } = event;
376
379
  if (model.kind === "collectionType" && model.options?.draftAndPublish) {
377
- const isEntryValid = await getEntryValidStatus(
378
- model.uid,
379
- result,
380
- {
381
- strapi: strapi2
382
- }
383
- );
380
+ const isEntryValid = await getEntryValidStatus(model.uid, result, {
381
+ strapi: strapi2
382
+ });
384
383
  await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
385
384
  where: {
386
385
  target_type: model.uid,
@@ -407,27 +406,23 @@ const bootstrap = async ({ strapi: strapi2 }) => {
407
406
  }
408
407
  }
409
408
  });
410
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
411
- getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
412
- strapi2.log.error(
413
- "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
414
- );
415
- throw err;
416
- });
417
- Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
418
- strapi2.webhookStore.addAllowedEvent(key, value);
419
- });
420
- }
409
+ getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
410
+ strapi2.log.error(
411
+ "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
412
+ );
413
+ throw err;
414
+ });
415
+ Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
416
+ strapi2.get("webhookStore").addAllowedEvent(key, value);
417
+ });
421
418
  }
422
419
  };
423
420
  const destroy = async ({ strapi: strapi2 }) => {
424
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
425
- const scheduledJobs = getService("scheduling", {
426
- strapi: strapi2
427
- }).getAll();
428
- for (const [, job] of scheduledJobs) {
429
- job.cancel();
430
- }
421
+ const scheduledJobs = getService("scheduling", {
422
+ strapi: strapi2
423
+ }).getAll();
424
+ for (const [, job] of scheduledJobs) {
425
+ job.cancel();
431
426
  }
432
427
  };
433
428
  const schema$1 = {
@@ -552,6 +547,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
552
547
  release: release2
553
548
  });
554
549
  };
550
+ const publishSingleTypeAction = async (uid, actionType, entryId) => {
551
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
552
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
553
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
554
+ const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
555
+ try {
556
+ if (actionType === "publish") {
557
+ await entityManagerService.publish(entry, uid);
558
+ } else {
559
+ await entityManagerService.unpublish(entry, uid);
560
+ }
561
+ } catch (error) {
562
+ if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
563
+ ;
564
+ else {
565
+ throw error;
566
+ }
567
+ }
568
+ };
569
+ const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
570
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
571
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
572
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
573
+ const entriesToPublish = await strapi2.entityService.findMany(uid, {
574
+ filters: {
575
+ id: {
576
+ $in: entriesToPublishIds
577
+ }
578
+ },
579
+ populate
580
+ });
581
+ const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
582
+ filters: {
583
+ id: {
584
+ $in: entriestoUnpublishIds
585
+ }
586
+ },
587
+ populate
588
+ });
589
+ if (entriesToPublish.length > 0) {
590
+ await entityManagerService.publishMany(entriesToPublish, uid);
591
+ }
592
+ if (entriesToUnpublish.length > 0) {
593
+ await entityManagerService.unpublishMany(entriesToUnpublish, uid);
594
+ }
595
+ };
596
+ const getFormattedActions = async (releaseId) => {
597
+ const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
598
+ where: {
599
+ release: {
600
+ id: releaseId
601
+ }
602
+ },
603
+ populate: {
604
+ entry: {
605
+ fields: ["id"]
606
+ }
607
+ }
608
+ });
609
+ if (actions.length === 0) {
610
+ throw new utils.errors.ValidationError("No entries to publish");
611
+ }
612
+ const collectionTypeActions = {};
613
+ const singleTypeActions = [];
614
+ for (const action of actions) {
615
+ const contentTypeUid = action.contentType;
616
+ if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
617
+ if (!collectionTypeActions[contentTypeUid]) {
618
+ collectionTypeActions[contentTypeUid] = {
619
+ entriesToPublishIds: [],
620
+ entriesToUnpublishIds: []
621
+ };
622
+ }
623
+ if (action.type === "publish") {
624
+ collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
625
+ } else {
626
+ collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
627
+ }
628
+ } else {
629
+ singleTypeActions.push({
630
+ uid: contentTypeUid,
631
+ action: action.type,
632
+ id: action.entry.id
633
+ });
634
+ }
635
+ }
636
+ return { collectionTypeActions, singleTypeActions };
637
+ };
555
638
  return {
556
639
  async create(releaseData, { user }) {
557
640
  const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
@@ -565,13 +648,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
565
648
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
566
649
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
567
650
  ]);
568
- const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
651
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
569
652
  data: {
570
653
  ...releaseWithCreatorFields,
571
654
  status: "empty"
572
655
  }
573
656
  });
574
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
657
+ if (releaseWithCreatorFields.scheduledAt) {
575
658
  const schedulingService = getService("scheduling", { strapi: strapi2 });
576
659
  await schedulingService.set(release2.id, release2.scheduledAt);
577
660
  }
@@ -579,28 +662,36 @@ const createReleaseService = ({ strapi: strapi2 }) => {
579
662
  return release2;
580
663
  },
581
664
  async findOne(id, query = {}) {
582
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
583
- ...query
665
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
666
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
667
+ ...dbQuery,
668
+ where: { id }
584
669
  });
585
670
  return release2;
586
671
  },
587
672
  findPage(query) {
588
- return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
589
- ...query,
673
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
674
+ return strapi2.db.query(RELEASE_MODEL_UID).findPage({
675
+ ...dbQuery,
590
676
  populate: {
591
677
  actions: {
592
- // @ts-expect-error Ignore missing properties
593
678
  count: true
594
679
  }
595
680
  }
596
681
  });
597
682
  },
598
- async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
683
+ async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
684
+ let entries = entriesIds;
685
+ if (!Array.isArray(entriesIds)) {
686
+ entries = [entriesIds];
687
+ }
599
688
  const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
600
689
  where: {
601
690
  actions: {
602
691
  target_type: contentTypeUid,
603
- target_id: entryId
692
+ target_id: {
693
+ $in: entries
694
+ }
604
695
  },
605
696
  releasedAt: {
606
697
  $null: true
@@ -611,18 +702,25 @@ const createReleaseService = ({ strapi: strapi2 }) => {
611
702
  actions: {
612
703
  where: {
613
704
  target_type: contentTypeUid,
614
- target_id: entryId
705
+ target_id: {
706
+ $in: entries
707
+ }
708
+ },
709
+ populate: {
710
+ entry: {
711
+ select: ["id"]
712
+ }
615
713
  }
616
714
  }
617
715
  }
618
716
  });
619
717
  return releases.map((release2) => {
620
718
  if (release2.actions?.length) {
621
- const [actionForEntry] = release2.actions;
719
+ const actionsForEntry = release2.actions;
622
720
  delete release2.actions;
623
721
  return {
624
722
  ...release2,
625
- action: actionForEntry
723
+ actions: actionsForEntry
626
724
  };
627
725
  }
628
726
  return release2;
@@ -681,28 +779,22 @@ const createReleaseService = ({ strapi: strapi2 }) => {
681
779
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
682
780
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
683
781
  ]);
684
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
782
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
685
783
  if (!release2) {
686
784
  throw new utils.errors.NotFoundError(`No release found for id ${id}`);
687
785
  }
688
786
  if (release2.releasedAt) {
689
787
  throw new utils.errors.ValidationError("Release already published");
690
788
  }
691
- const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
692
- /*
693
- * The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
694
- * is not compatible with the type we are passing here: UpdateRelease.Request['body']
695
- */
696
- // @ts-expect-error see above
789
+ const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
790
+ where: { id },
697
791
  data: releaseWithCreatorFields
698
792
  });
699
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
700
- const schedulingService = getService("scheduling", { strapi: strapi2 });
701
- if (releaseData.scheduledAt) {
702
- await schedulingService.set(id, releaseData.scheduledAt);
703
- } else if (release2.scheduledAt) {
704
- schedulingService.cancel(id);
705
- }
793
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
794
+ if (releaseData.scheduledAt) {
795
+ await schedulingService.set(id, releaseData.scheduledAt);
796
+ } else if (release2.scheduledAt) {
797
+ schedulingService.cancel(id);
706
798
  }
707
799
  this.updateReleaseStatus(id);
708
800
  strapi2.telemetry.send("didUpdateContentRelease");
@@ -716,7 +808,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
716
808
  validateEntryContentType(action.entry.contentType),
717
809
  validateUniqueEntry(releaseId, action)
718
810
  ]);
719
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
811
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
720
812
  if (!release2) {
721
813
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
722
814
  }
@@ -726,7 +818,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
726
818
  const { entry, type } = action;
727
819
  const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
728
820
  const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
729
- const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
821
+ const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
730
822
  data: {
731
823
  type,
732
824
  contentType: entry.contentType,
@@ -739,32 +831,35 @@ const createReleaseService = ({ strapi: strapi2 }) => {
739
831
  },
740
832
  release: releaseId
741
833
  },
742
- populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
834
+ populate: { release: { select: ["id"] }, entry: { select: ["id"] } }
743
835
  });
744
836
  this.updateReleaseStatus(releaseId);
745
837
  return releaseAction2;
746
838
  },
747
839
  async findActions(releaseId, query) {
748
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
749
- fields: ["id"]
840
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
841
+ where: { id: releaseId },
842
+ select: ["id"]
750
843
  });
751
844
  if (!release2) {
752
845
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
753
846
  }
754
- return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
755
- ...query,
847
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
848
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
849
+ ...dbQuery,
756
850
  populate: {
757
851
  entry: {
758
852
  populate: "*"
759
853
  }
760
854
  },
761
- filters: {
855
+ where: {
762
856
  release: releaseId
763
857
  }
764
858
  });
765
859
  },
766
860
  async countActions(query) {
767
- return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
861
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
862
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
768
863
  },
769
864
  async groupActions(actions, groupBy) {
770
865
  const contentTypeUids = actions.reduce((acc, action) => {
@@ -773,9 +868,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
773
868
  }
774
869
  return acc;
775
870
  }, []);
776
- const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
777
- contentTypeUids
778
- );
871
+ const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(contentTypeUids);
779
872
  const allLocalesDictionary = await this.getLocalesDataForActions();
780
873
  const formattedData = actions.map((action) => {
781
874
  const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
@@ -845,10 +938,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
845
938
  return componentsMap;
846
939
  },
847
940
  async delete(releaseId) {
848
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
941
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
942
+ where: { id: releaseId },
849
943
  populate: {
850
944
  actions: {
851
- fields: ["id"]
945
+ select: ["id"]
852
946
  }
853
947
  }
854
948
  });
@@ -866,9 +960,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
866
960
  }
867
961
  }
868
962
  });
869
- await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
963
+ await strapi2.db.query(RELEASE_MODEL_UID).delete({
964
+ where: {
965
+ id: releaseId
966
+ }
967
+ });
870
968
  });
871
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
969
+ if (release2.scheduledAt) {
872
970
  const schedulingService = getService("scheduling", { strapi: strapi2 });
873
971
  await schedulingService.cancel(release2.id);
874
972
  }
@@ -876,145 +974,69 @@ const createReleaseService = ({ strapi: strapi2 }) => {
876
974
  return release2;
877
975
  },
878
976
  async publish(releaseId) {
879
- try {
880
- const releaseWithPopulatedActionEntries = await strapi2.entityService.findOne(
881
- RELEASE_MODEL_UID,
882
- releaseId,
883
- {
884
- populate: {
885
- actions: {
886
- populate: {
887
- entry: {
888
- fields: ["id"]
889
- }
890
- }
891
- }
892
- }
893
- }
894
- );
895
- if (!releaseWithPopulatedActionEntries) {
977
+ const {
978
+ release: release2,
979
+ error
980
+ } = await strapi2.db.transaction(async ({ trx }) => {
981
+ const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
982
+ if (!lockedRelease) {
896
983
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
897
984
  }
898
- if (releaseWithPopulatedActionEntries.releasedAt) {
985
+ if (lockedRelease.releasedAt) {
899
986
  throw new utils.errors.ValidationError("Release already published");
900
987
  }
901
- if (releaseWithPopulatedActionEntries.actions.length === 0) {
902
- throw new utils.errors.ValidationError("No entries to publish");
988
+ if (lockedRelease.status === "failed") {
989
+ throw new utils.errors.ValidationError("Release failed to publish");
903
990
  }
904
- const collectionTypeActions = {};
905
- const singleTypeActions = [];
906
- for (const action of releaseWithPopulatedActionEntries.actions) {
907
- const contentTypeUid = action.contentType;
908
- if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
909
- if (!collectionTypeActions[contentTypeUid]) {
910
- collectionTypeActions[contentTypeUid] = {
911
- entriestoPublishIds: [],
912
- entriesToUnpublishIds: []
913
- };
914
- }
915
- if (action.type === "publish") {
916
- collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
917
- } else {
918
- collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
919
- }
920
- } else {
921
- singleTypeActions.push({
922
- uid: contentTypeUid,
923
- action: action.type,
924
- id: action.entry.id
925
- });
926
- }
927
- }
928
- const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
929
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
930
- await strapi2.db.transaction(async () => {
931
- for (const { uid, action, id } of singleTypeActions) {
932
- const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
933
- const entry = await strapi2.entityService.findOne(uid, id, { populate });
934
- try {
935
- if (action === "publish") {
936
- await entityManagerService.publish(entry, uid);
937
- } else {
938
- await entityManagerService.unpublish(entry, uid);
939
- }
940
- } catch (error) {
941
- if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
942
- } else {
943
- throw error;
944
- }
945
- }
946
- }
947
- for (const contentTypeUid of Object.keys(collectionTypeActions)) {
948
- const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
949
- const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
950
- const entriesToPublish = await strapi2.entityService.findMany(
951
- contentTypeUid,
952
- {
953
- filters: {
954
- id: {
955
- $in: entriestoPublishIds
956
- }
957
- },
958
- populate
959
- }
960
- );
961
- const entriesToUnpublish = await strapi2.entityService.findMany(
962
- contentTypeUid,
963
- {
964
- filters: {
965
- id: {
966
- $in: entriesToUnpublishIds
967
- }
968
- },
969
- populate
970
- }
971
- );
972
- if (entriesToPublish.length > 0) {
973
- await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
991
+ try {
992
+ strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
993
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(releaseId);
994
+ await strapi2.db.transaction(async () => {
995
+ for (const { uid, action, id } of singleTypeActions) {
996
+ await publishSingleTypeAction(uid, action, id);
974
997
  }
975
- if (entriesToUnpublish.length > 0) {
976
- await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
998
+ for (const contentTypeUid of Object.keys(collectionTypeActions)) {
999
+ const uid = contentTypeUid;
1000
+ await publishCollectionTypeAction(
1001
+ uid,
1002
+ collectionTypeActions[uid].entriesToPublishIds,
1003
+ collectionTypeActions[uid].entriesToUnpublishIds
1004
+ );
977
1005
  }
978
- }
979
- });
980
- const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
981
- data: {
982
- /*
983
- * The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
984
- */
985
- // @ts-expect-error see above
986
- releasedAt: /* @__PURE__ */ new Date()
987
- },
988
- populate: {
989
- actions: {
990
- // @ts-expect-error is not expecting count but it is working
991
- count: true
1006
+ });
1007
+ const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
1008
+ where: {
1009
+ id: releaseId
1010
+ },
1011
+ data: {
1012
+ status: "done",
1013
+ releasedAt: /* @__PURE__ */ new Date()
992
1014
  }
993
- }
994
- });
995
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
1015
+ });
996
1016
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
997
1017
  isPublished: true,
998
- release: release2
1018
+ release: release22
999
1019
  });
1000
- }
1001
- strapi2.telemetry.send("didPublishContentRelease");
1002
- return release2;
1003
- } catch (error) {
1004
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
1020
+ strapi2.telemetry.send("didPublishContentRelease");
1021
+ return { release: release22, error: null };
1022
+ } catch (error2) {
1005
1023
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
1006
1024
  isPublished: false,
1007
- error
1025
+ error: error2
1008
1026
  });
1009
- }
1010
- strapi2.db.query(RELEASE_MODEL_UID).update({
1011
- where: { id: releaseId },
1012
- data: {
1027
+ await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
1013
1028
  status: "failed"
1014
- }
1015
- });
1029
+ }).transacting(trx).execute();
1030
+ return {
1031
+ release: null,
1032
+ error: error2
1033
+ };
1034
+ }
1035
+ });
1036
+ if (error instanceof Error) {
1016
1037
  throw error;
1017
1038
  }
1039
+ return release2;
1018
1040
  },
1019
1041
  async updateAction(actionId, releaseId, update) {
1020
1042
  const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
@@ -1101,10 +1123,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1101
1123
  }
1102
1124
  };
1103
1125
  };
1126
+ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
1127
+ constructor(message) {
1128
+ super(message);
1129
+ this.name = "AlreadyOnReleaseError";
1130
+ }
1131
+ }
1104
1132
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1105
1133
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1106
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
1107
- populate: { actions: { populate: { entry: { fields: ["id"] } } } }
1134
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
1135
+ where: {
1136
+ id: releaseId
1137
+ },
1138
+ populate: { actions: { populate: { entry: { select: ["id"] } } } }
1108
1139
  });
1109
1140
  if (!release2) {
1110
1141
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
@@ -1113,7 +1144,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1113
1144
  (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1114
1145
  );
1115
1146
  if (isEntryInRelease) {
1116
- throw new utils.errors.ValidationError(
1147
+ throw new AlreadyOnReleaseError(
1117
1148
  `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1118
1149
  );
1119
1150
  }
@@ -1130,10 +1161,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1130
1161
  }
1131
1162
  },
1132
1163
  async validatePendingReleasesLimit() {
1133
- const maximumPendingReleases = (
1134
- // @ts-expect-error - options is not typed into features
1135
- EE__default.default.features.get("cms-content-releases")?.options?.maximumReleases || 3
1136
- );
1164
+ const featureCfg = strapi2.ee.features.get("cms-content-releases");
1165
+ const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
1137
1166
  const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
1138
1167
  filters: {
1139
1168
  releasedAt: {
@@ -1146,8 +1175,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1146
1175
  }
1147
1176
  },
1148
1177
  async validateUniqueNameForPendingRelease(name, id) {
1149
- const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
1150
- filters: {
1178
+ const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
1179
+ where: {
1151
1180
  releasedAt: {
1152
1181
  $null: true
1153
1182
  },
@@ -1176,7 +1205,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1176
1205
  }
1177
1206
  const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
1178
1207
  try {
1179
- await getService("release").publish(releaseId);
1208
+ await getService("release", { strapi: strapi2 }).publish(releaseId);
1180
1209
  } catch (error) {
1181
1210
  }
1182
1211
  this.cancel(releaseId);
@@ -1221,7 +1250,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1221
1250
  const services = {
1222
1251
  release: createReleaseService,
1223
1252
  "release-validation": createReleaseValidationService,
1224
- ...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
1253
+ scheduling: createSchedulingService
1225
1254
  };
1226
1255
  const RELEASE_SCHEMA = yup__namespace.object().shape({
1227
1256
  name: yup__namespace.string().trim().required(),
@@ -1246,7 +1275,7 @@ const RELEASE_SCHEMA = yup__namespace.object().shape({
1246
1275
  const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
1247
1276
  const releaseController = {
1248
1277
  async findMany(ctx) {
1249
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1278
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1250
1279
  ability: ctx.state.userAbility,
1251
1280
  model: RELEASE_MODEL_UID
1252
1281
  });
@@ -1274,7 +1303,7 @@ const releaseController = {
1274
1303
  }
1275
1304
  };
1276
1305
  });
1277
- const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
1306
+ const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1278
1307
  where: {
1279
1308
  releasedAt: null
1280
1309
  }
@@ -1296,7 +1325,7 @@ const releaseController = {
1296
1325
  });
1297
1326
  const sanitizedRelease = {
1298
1327
  ...release2,
1299
- createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
1328
+ createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
1300
1329
  };
1301
1330
  const data = {
1302
1331
  ...sanitizedRelease,
@@ -1308,19 +1337,48 @@ const releaseController = {
1308
1337
  };
1309
1338
  ctx.body = { data };
1310
1339
  },
1340
+ async mapEntriesToReleases(ctx) {
1341
+ const { contentTypeUid, entriesIds } = ctx.query;
1342
+ if (!contentTypeUid || !entriesIds) {
1343
+ throw new utils.errors.ValidationError("Missing required query parameters");
1344
+ }
1345
+ const releaseService = getService("release", { strapi });
1346
+ const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1347
+ contentTypeUid,
1348
+ entriesIds
1349
+ );
1350
+ const mappedEntriesInReleases = releasesWithActions.reduce(
1351
+ // TODO: Fix for v5 removed mappedEntriedToRelease
1352
+ (acc, release2) => {
1353
+ release2.actions.forEach((action) => {
1354
+ if (!acc[action.entry.id]) {
1355
+ acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
1356
+ } else {
1357
+ acc[action.entry.id].push({ id: release2.id, name: release2.name });
1358
+ }
1359
+ });
1360
+ return acc;
1361
+ },
1362
+ // TODO: Fix for v5 removed mappedEntriedToRelease
1363
+ {}
1364
+ );
1365
+ ctx.body = {
1366
+ data: mappedEntriesInReleases
1367
+ };
1368
+ },
1311
1369
  async create(ctx) {
1312
1370
  const user = ctx.state.user;
1313
1371
  const releaseArgs = ctx.request.body;
1314
1372
  await validateRelease(releaseArgs);
1315
1373
  const releaseService = getService("release", { strapi });
1316
1374
  const release2 = await releaseService.create(releaseArgs, { user });
1317
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1375
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1318
1376
  ability: ctx.state.userAbility,
1319
1377
  model: RELEASE_MODEL_UID
1320
1378
  });
1321
- ctx.body = {
1379
+ ctx.created({
1322
1380
  data: await permissionsManager.sanitizeOutput(release2)
1323
- };
1381
+ });
1324
1382
  },
1325
1383
  async update(ctx) {
1326
1384
  const user = ctx.state.user;
@@ -1329,7 +1387,7 @@ const releaseController = {
1329
1387
  await validateRelease(releaseArgs);
1330
1388
  const releaseService = getService("release", { strapi });
1331
1389
  const release2 = await releaseService.update(id, releaseArgs, { user });
1332
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1390
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1333
1391
  ability: ctx.state.userAbility,
1334
1392
  model: RELEASE_MODEL_UID
1335
1393
  });
@@ -1393,13 +1451,45 @@ const releaseActionController = {
1393
1451
  await validateReleaseAction(releaseActionArgs);
1394
1452
  const releaseService = getService("release", { strapi });
1395
1453
  const releaseAction2 = await releaseService.createAction(releaseId, releaseActionArgs);
1396
- ctx.body = {
1454
+ ctx.created({
1397
1455
  data: releaseAction2
1398
- };
1456
+ });
1457
+ },
1458
+ async createMany(ctx) {
1459
+ const releaseId = ctx.params.releaseId;
1460
+ const releaseActionsArgs = ctx.request.body;
1461
+ await Promise.all(
1462
+ releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1463
+ );
1464
+ const releaseService = getService("release", { strapi });
1465
+ const releaseActions = await strapi.db.transaction(async () => {
1466
+ const releaseActions2 = await Promise.all(
1467
+ releaseActionsArgs.map(async (releaseActionArgs) => {
1468
+ try {
1469
+ const action = await releaseService.createAction(releaseId, releaseActionArgs);
1470
+ return action;
1471
+ } catch (error) {
1472
+ if (error instanceof AlreadyOnReleaseError) {
1473
+ return null;
1474
+ }
1475
+ throw error;
1476
+ }
1477
+ })
1478
+ );
1479
+ return releaseActions2;
1480
+ });
1481
+ const newReleaseActions = releaseActions.filter((action) => action !== null);
1482
+ ctx.created({
1483
+ data: newReleaseActions,
1484
+ meta: {
1485
+ entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1486
+ totalEntries: releaseActions.length
1487
+ }
1488
+ });
1399
1489
  },
1400
1490
  async findMany(ctx) {
1401
1491
  const releaseId = ctx.params.releaseId;
1402
- const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1492
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1403
1493
  ability: ctx.state.userAbility,
1404
1494
  model: RELEASE_ACTION_MODEL_UID
1405
1495
  });
@@ -1413,14 +1503,14 @@ const releaseActionController = {
1413
1503
  if (acc[action.contentType]) {
1414
1504
  return acc;
1415
1505
  }
1416
- const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
1506
+ const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
1417
1507
  ability: ctx.state.userAbility,
1418
1508
  model: action.contentType
1419
1509
  });
1420
1510
  acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
1421
1511
  return acc;
1422
1512
  }, {});
1423
- const sanitizedResults = await utils.mapAsync(results, async (action) => ({
1513
+ const sanitizedResults = await utils.async.map(results, async (action) => ({
1424
1514
  ...action,
1425
1515
  entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
1426
1516
  }));
@@ -1465,6 +1555,22 @@ const controllers = { release: releaseController, "release-action": releaseActio
1465
1555
  const release = {
1466
1556
  type: "admin",
1467
1557
  routes: [
1558
+ {
1559
+ method: "GET",
1560
+ path: "/mapEntriesToReleases",
1561
+ handler: "release.mapEntriesToReleases",
1562
+ config: {
1563
+ policies: [
1564
+ "admin::isAuthenticatedAdmin",
1565
+ {
1566
+ name: "admin::hasPermissions",
1567
+ config: {
1568
+ actions: ["plugin::content-releases.read"]
1569
+ }
1570
+ }
1571
+ ]
1572
+ }
1573
+ },
1468
1574
  {
1469
1575
  method: "POST",
1470
1576
  path: "/",
@@ -1582,6 +1688,22 @@ const releaseAction = {
1582
1688
  ]
1583
1689
  }
1584
1690
  },
1691
+ {
1692
+ method: "POST",
1693
+ path: "/:releaseId/actions/bulk",
1694
+ handler: "release-action.createMany",
1695
+ config: {
1696
+ policies: [
1697
+ "admin::isAuthenticatedAdmin",
1698
+ {
1699
+ name: "admin::hasPermissions",
1700
+ config: {
1701
+ actions: ["plugin::content-releases.create-action"]
1702
+ }
1703
+ }
1704
+ ]
1705
+ }
1706
+ },
1585
1707
  {
1586
1708
  method: "GET",
1587
1709
  path: "/:releaseId/actions",
@@ -1636,9 +1758,8 @@ const routes = {
1636
1758
  release,
1637
1759
  "release-action": releaseAction
1638
1760
  };
1639
- const { features } = require("@strapi/strapi/dist/utils/ee");
1640
1761
  const getPlugin = () => {
1641
- if (features.isEnabled("cms-content-releases")) {
1762
+ if (strapi.ee.features.isEnabled("cms-content-releases")) {
1642
1763
  return {
1643
1764
  register,
1644
1765
  bootstrap,