@strapi/content-releases 4.20.5 → 5.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/_chunks/{App-bpzO2Ljh.mjs → App-dbdAcsz_.mjs} +286 -288
  2. package/dist/_chunks/App-dbdAcsz_.mjs.map +1 -0
  3. package/dist/_chunks/{App-p8aKBitd.js → App-zwe_jKPv.js} +292 -294
  4. package/dist/_chunks/App-zwe_jKPv.js.map +1 -0
  5. package/dist/_chunks/{en-WuuhP6Bn.mjs → en-RdapH-9X.mjs} +1 -2
  6. package/dist/_chunks/en-RdapH-9X.mjs.map +1 -0
  7. package/dist/_chunks/{en-gcJJ5htG.js → en-faJDuv3q.js} +1 -2
  8. package/dist/_chunks/en-faJDuv3q.js.map +1 -0
  9. package/dist/_chunks/{index-AECgcaDa.mjs → index-RBaVMtyr.mjs} +78 -69
  10. package/dist/_chunks/index-RBaVMtyr.mjs.map +1 -0
  11. package/dist/_chunks/{index-fP3qoWZ4.js → index-TBrVNrv9.js} +76 -67
  12. package/dist/_chunks/index-TBrVNrv9.js.map +1 -0
  13. package/dist/admin/index.js +1 -14
  14. package/dist/admin/index.js.map +1 -1
  15. package/dist/admin/index.mjs +1 -14
  16. package/dist/admin/index.mjs.map +1 -1
  17. package/dist/admin/src/components/CMReleasesContainer.d.ts +1 -0
  18. package/dist/admin/src/components/RelativeTime.d.ts +28 -0
  19. package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
  20. package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
  21. package/dist/admin/src/components/ReleaseModal.d.ts +16 -0
  22. package/dist/admin/src/constants.d.ts +58 -0
  23. package/dist/admin/src/index.d.ts +3 -0
  24. package/dist/admin/src/pages/App.d.ts +1 -0
  25. package/dist/admin/src/pages/PurchaseContentReleases.d.ts +2 -0
  26. package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +2 -0
  27. package/dist/admin/src/pages/ReleasesPage.d.ts +8 -0
  28. package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +181 -0
  29. package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +39 -0
  30. package/dist/admin/src/pluginId.d.ts +1 -0
  31. package/dist/admin/src/services/axios.d.ts +29 -0
  32. package/dist/admin/src/services/release.d.ts +369 -0
  33. package/dist/admin/src/store/hooks.d.ts +7 -0
  34. package/dist/admin/src/utils/time.d.ts +1 -0
  35. package/dist/server/index.js +277 -232
  36. package/dist/server/index.js.map +1 -1
  37. package/dist/server/index.mjs +278 -232
  38. package/dist/server/index.mjs.map +1 -1
  39. package/dist/server/src/bootstrap.d.ts +5 -0
  40. package/dist/server/src/bootstrap.d.ts.map +1 -0
  41. package/dist/server/src/constants.d.ts +12 -0
  42. package/dist/server/src/constants.d.ts.map +1 -0
  43. package/dist/server/src/content-types/index.d.ts +99 -0
  44. package/dist/server/src/content-types/index.d.ts.map +1 -0
  45. package/dist/server/src/content-types/release/index.d.ts +48 -0
  46. package/dist/server/src/content-types/release/index.d.ts.map +1 -0
  47. package/dist/server/src/content-types/release/schema.d.ts +47 -0
  48. package/dist/server/src/content-types/release/schema.d.ts.map +1 -0
  49. package/dist/server/src/content-types/release-action/index.d.ts +50 -0
  50. package/dist/server/src/content-types/release-action/index.d.ts.map +1 -0
  51. package/dist/server/src/content-types/release-action/schema.d.ts +49 -0
  52. package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -0
  53. package/dist/server/src/controllers/index.d.ts +19 -0
  54. package/dist/server/src/controllers/index.d.ts.map +1 -0
  55. package/dist/server/src/controllers/release-action.d.ts +10 -0
  56. package/dist/server/src/controllers/release-action.d.ts.map +1 -0
  57. package/dist/server/src/controllers/release.d.ts +11 -0
  58. package/dist/server/src/controllers/release.d.ts.map +1 -0
  59. package/dist/server/src/controllers/validation/release-action.d.ts +3 -0
  60. package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -0
  61. package/dist/server/src/controllers/validation/release.d.ts +2 -0
  62. package/dist/server/src/controllers/validation/release.d.ts.map +1 -0
  63. package/dist/server/src/destroy.d.ts +5 -0
  64. package/dist/server/src/destroy.d.ts.map +1 -0
  65. package/dist/server/src/index.d.ts +2095 -0
  66. package/dist/server/src/index.d.ts.map +1 -0
  67. package/dist/server/src/migrations/index.d.ts +12 -0
  68. package/dist/server/src/migrations/index.d.ts.map +1 -0
  69. package/dist/server/src/register.d.ts +5 -0
  70. package/dist/server/src/register.d.ts.map +1 -0
  71. package/dist/server/src/routes/index.d.ts +35 -0
  72. package/dist/server/src/routes/index.d.ts.map +1 -0
  73. package/dist/server/src/routes/release-action.d.ts +18 -0
  74. package/dist/server/src/routes/release-action.d.ts.map +1 -0
  75. package/dist/server/src/routes/release.d.ts +18 -0
  76. package/dist/server/src/routes/release.d.ts.map +1 -0
  77. package/dist/server/src/services/index.d.ts +1826 -0
  78. package/dist/server/src/services/index.d.ts.map +1 -0
  79. package/dist/server/src/services/release.d.ts +66 -0
  80. package/dist/server/src/services/release.d.ts.map +1 -0
  81. package/dist/server/src/services/scheduling.d.ts +18 -0
  82. package/dist/server/src/services/scheduling.d.ts.map +1 -0
  83. package/dist/server/src/services/validation.d.ts +18 -0
  84. package/dist/server/src/services/validation.d.ts.map +1 -0
  85. package/dist/server/src/utils/index.d.ts +14 -0
  86. package/dist/server/src/utils/index.d.ts.map +1 -0
  87. package/dist/shared/contracts/release-actions.d.ts +131 -0
  88. package/dist/shared/contracts/release-actions.d.ts.map +1 -0
  89. package/dist/shared/contracts/releases.d.ts +166 -0
  90. package/dist/shared/contracts/releases.d.ts.map +1 -0
  91. package/dist/shared/types.d.ts +24 -0
  92. package/dist/shared/types.d.ts.map +1 -0
  93. package/dist/shared/validation-schemas.d.ts +2 -0
  94. package/dist/shared/validation-schemas.d.ts.map +1 -0
  95. package/package.json +22 -28
  96. package/dist/_chunks/App-bpzO2Ljh.mjs.map +0 -1
  97. package/dist/_chunks/App-p8aKBitd.js.map +0 -1
  98. package/dist/_chunks/en-WuuhP6Bn.mjs.map +0 -1
  99. package/dist/_chunks/en-gcJJ5htG.js.map +0 -1
  100. package/dist/_chunks/index-AECgcaDa.mjs.map +0 -1
  101. 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";
@@ -84,7 +82,10 @@ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
84
82
  const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
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
91
  const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
@@ -101,28 +102,10 @@ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } =
101
102
  return false;
102
103
  }
103
104
  };
104
- async function deleteActionsOnDisableDraftAndPublish({
105
- oldContentTypes,
106
- contentTypes: contentTypes2
107
- }) {
108
- if (!oldContentTypes) {
109
- return;
110
- }
111
- for (const uid in contentTypes2) {
112
- if (!oldContentTypes[uid]) {
113
- continue;
114
- }
115
- const oldContentType = oldContentTypes[uid];
116
- const contentType = contentTypes2[uid];
117
- if (utils.contentTypes.hasDraftAndPublish(oldContentType) && !utils.contentTypes.hasDraftAndPublish(contentType)) {
118
- await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
119
- }
120
- }
121
- }
122
105
  async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
123
106
  const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
124
107
  if (deletedContentTypes.length) {
125
- await utils.mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
108
+ await utils.async.map(deletedContentTypes, async (deletedContentTypeUID) => {
126
109
  return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
127
110
  });
128
111
  }
@@ -141,7 +124,7 @@ async function migrateIsValidAndStatusReleases() {
141
124
  }
142
125
  }
143
126
  });
144
- utils.mapAsync(releasesWithoutStatus, async (release2) => {
127
+ utils.async.map(releasesWithoutStatus, async (release2) => {
145
128
  const actions = release2.actions;
146
129
  const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
147
130
  for (const action of notValidatedActions) {
@@ -172,7 +155,7 @@ async function migrateIsValidAndStatusReleases() {
172
155
  }
173
156
  }
174
157
  });
175
- utils.mapAsync(publishedReleases, async (release2) => {
158
+ utils.async.map(publishedReleases, async (release2) => {
176
159
  return strapi.db.query(RELEASE_MODEL_UID).update({
177
160
  where: {
178
161
  id: release2.id
@@ -185,11 +168,9 @@ async function migrateIsValidAndStatusReleases() {
185
168
  }
186
169
  async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
187
170
  if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
188
- const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
189
- (uid) => oldContentTypes[uid]?.options?.draftAndPublish
190
- );
171
+ const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes);
191
172
  const releasesAffected = /* @__PURE__ */ new Set();
192
- utils.mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
173
+ utils.async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
193
174
  const oldContentType = oldContentTypes[contentTypeUID];
194
175
  const contentType = contentTypes2[contentTypeUID];
195
176
  if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
@@ -202,7 +183,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
202
183
  release: true
203
184
  }
204
185
  });
205
- await utils.mapAsync(actions, async (action) => {
186
+ await utils.async.map(actions, async (action) => {
206
187
  if (action.entry && action.release) {
207
188
  const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
208
189
  strapi
@@ -225,7 +206,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
225
206
  });
226
207
  }
227
208
  }).then(() => {
228
- utils.mapAsync(releasesAffected, async (releaseId) => {
209
+ utils.async.map(releasesAffected, async (releaseId) => {
229
210
  return getService("release", { strapi }).updateReleaseStatus(releaseId);
230
211
  });
231
212
  });
@@ -271,11 +252,10 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
271
252
  }
272
253
  }
273
254
  }
274
- const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
275
255
  const register = async ({ strapi: strapi2 }) => {
276
- if (features$2.isEnabled("cms-content-releases")) {
256
+ if (strapi2.ee.features.isEnabled("cms-content-releases")) {
277
257
  await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
278
- strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish).register(disableContentTypeLocalized);
258
+ strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized);
279
259
  strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
280
260
  }
281
261
  if (strapi2.plugin("graphql")) {
@@ -284,12 +264,9 @@ const register = async ({ strapi: strapi2 }) => {
284
264
  graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
285
265
  }
286
266
  };
287
- const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
288
267
  const bootstrap = async ({ strapi: strapi2 }) => {
289
- if (features$1.isEnabled("cms-content-releases")) {
290
- const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
291
- (uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
292
- );
268
+ if (strapi2.ee.features.isEnabled("cms-content-releases")) {
269
+ const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes);
293
270
  strapi2.db.lifecycles.subscribe({
294
271
  models: contentTypesWithDraftAndPublish,
295
272
  async afterDelete(event) {
@@ -325,7 +302,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
325
302
  */
326
303
  async beforeDeleteMany(event) {
327
304
  const { model, params } = event;
328
- if (model.kind === "collectionType" && model.options?.draftAndPublish) {
305
+ if (model.kind === "collectionType") {
329
306
  const { where } = params;
330
307
  const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
331
308
  event.state.entriesToDelete = entriesToDelete;
@@ -407,27 +384,23 @@ const bootstrap = async ({ strapi: strapi2 }) => {
407
384
  }
408
385
  }
409
386
  });
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
- }
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
+ });
421
396
  }
422
397
  };
423
398
  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
- }
399
+ const scheduledJobs = getService("scheduling", {
400
+ strapi: strapi2
401
+ }).getAll();
402
+ for (const [, job] of scheduledJobs) {
403
+ job.cancel();
431
404
  }
432
405
  };
433
406
  const schema$1 = {
@@ -552,6 +525,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
552
525
  release: release2
553
526
  });
554
527
  };
528
+ const publishSingleTypeAction = async (uid, actionType, entryId) => {
529
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
530
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
531
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
532
+ const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
533
+ try {
534
+ if (actionType === "publish") {
535
+ await entityManagerService.publish(entry, uid);
536
+ } else {
537
+ await entityManagerService.unpublish(entry, uid);
538
+ }
539
+ } catch (error) {
540
+ if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
541
+ ;
542
+ else {
543
+ throw error;
544
+ }
545
+ }
546
+ };
547
+ const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
548
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
549
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
550
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
551
+ const entriesToPublish = await strapi2.entityService.findMany(uid, {
552
+ filters: {
553
+ id: {
554
+ $in: entriesToPublishIds
555
+ }
556
+ },
557
+ populate
558
+ });
559
+ const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
560
+ filters: {
561
+ id: {
562
+ $in: entriestoUnpublishIds
563
+ }
564
+ },
565
+ populate
566
+ });
567
+ if (entriesToPublish.length > 0) {
568
+ await entityManagerService.publishMany(entriesToPublish, uid);
569
+ }
570
+ if (entriesToUnpublish.length > 0) {
571
+ await entityManagerService.unpublishMany(entriesToUnpublish, uid);
572
+ }
573
+ };
574
+ const getFormattedActions = async (releaseId) => {
575
+ const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
576
+ where: {
577
+ release: {
578
+ id: releaseId
579
+ }
580
+ },
581
+ populate: {
582
+ entry: {
583
+ fields: ["id"]
584
+ }
585
+ }
586
+ });
587
+ if (actions.length === 0) {
588
+ throw new utils.errors.ValidationError("No entries to publish");
589
+ }
590
+ const collectionTypeActions = {};
591
+ const singleTypeActions = [];
592
+ for (const action of actions) {
593
+ const contentTypeUid = action.contentType;
594
+ if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
595
+ if (!collectionTypeActions[contentTypeUid]) {
596
+ collectionTypeActions[contentTypeUid] = {
597
+ entriesToPublishIds: [],
598
+ entriesToUnpublishIds: []
599
+ };
600
+ }
601
+ if (action.type === "publish") {
602
+ collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
603
+ } else {
604
+ collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
605
+ }
606
+ } else {
607
+ singleTypeActions.push({
608
+ uid: contentTypeUid,
609
+ action: action.type,
610
+ id: action.entry.id
611
+ });
612
+ }
613
+ }
614
+ return { collectionTypeActions, singleTypeActions };
615
+ };
555
616
  return {
556
617
  async create(releaseData, { user }) {
557
618
  const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
@@ -565,13 +626,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
565
626
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
566
627
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
567
628
  ]);
568
- const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
629
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
569
630
  data: {
570
631
  ...releaseWithCreatorFields,
571
632
  status: "empty"
572
633
  }
573
634
  });
574
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
635
+ if (releaseWithCreatorFields.scheduledAt) {
575
636
  const schedulingService = getService("scheduling", { strapi: strapi2 });
576
637
  await schedulingService.set(release2.id, release2.scheduledAt);
577
638
  }
@@ -579,17 +640,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
579
640
  return release2;
580
641
  },
581
642
  async findOne(id, query = {}) {
582
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
583
- ...query
643
+ const dbQuery = utils.convertQueryParams.transformParamsToQuery(RELEASE_MODEL_UID, query);
644
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
645
+ ...dbQuery,
646
+ where: { id }
584
647
  });
585
648
  return release2;
586
649
  },
587
650
  findPage(query) {
588
- return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
589
- ...query,
651
+ const dbQuery = utils.convertQueryParams.transformParamsToQuery(RELEASE_MODEL_UID, query ?? {});
652
+ return strapi2.db.query(RELEASE_MODEL_UID).findPage({
653
+ ...dbQuery,
590
654
  populate: {
591
655
  actions: {
592
- // @ts-expect-error Ignore missing properties
593
656
  count: true
594
657
  }
595
658
  }
@@ -681,28 +744,22 @@ const createReleaseService = ({ strapi: strapi2 }) => {
681
744
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
682
745
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
683
746
  ]);
684
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
747
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
685
748
  if (!release2) {
686
749
  throw new utils.errors.NotFoundError(`No release found for id ${id}`);
687
750
  }
688
751
  if (release2.releasedAt) {
689
752
  throw new utils.errors.ValidationError("Release already published");
690
753
  }
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
754
+ const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
755
+ where: { id },
697
756
  data: releaseWithCreatorFields
698
757
  });
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
- }
758
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
759
+ if (releaseData.scheduledAt) {
760
+ await schedulingService.set(id, releaseData.scheduledAt);
761
+ } else if (release2.scheduledAt) {
762
+ schedulingService.cancel(id);
706
763
  }
707
764
  this.updateReleaseStatus(id);
708
765
  strapi2.telemetry.send("didUpdateContentRelease");
@@ -716,7 +773,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
716
773
  validateEntryContentType(action.entry.contentType),
717
774
  validateUniqueEntry(releaseId, action)
718
775
  ]);
719
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
776
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
720
777
  if (!release2) {
721
778
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
722
779
  }
@@ -726,7 +783,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
726
783
  const { entry, type } = action;
727
784
  const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
728
785
  const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
729
- const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
786
+ const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
730
787
  data: {
731
788
  type,
732
789
  contentType: entry.contentType,
@@ -739,32 +796,41 @@ const createReleaseService = ({ strapi: strapi2 }) => {
739
796
  },
740
797
  release: releaseId
741
798
  },
742
- populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
799
+ populate: { release: { select: ["id"] }, entry: { select: ["id"] } }
743
800
  });
744
801
  this.updateReleaseStatus(releaseId);
745
802
  return releaseAction2;
746
803
  },
747
804
  async findActions(releaseId, query) {
748
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
749
- fields: ["id"]
805
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
806
+ where: { id: releaseId },
807
+ select: ["id"]
750
808
  });
751
809
  if (!release2) {
752
810
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
753
811
  }
754
- return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
755
- ...query,
812
+ const dbQuery = utils.convertQueryParams.transformParamsToQuery(
813
+ RELEASE_ACTION_MODEL_UID,
814
+ query ?? {}
815
+ );
816
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
817
+ ...dbQuery,
756
818
  populate: {
757
819
  entry: {
758
820
  populate: "*"
759
821
  }
760
822
  },
761
- filters: {
823
+ where: {
762
824
  release: releaseId
763
825
  }
764
826
  });
765
827
  },
766
828
  async countActions(query) {
767
- return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
829
+ const dbQuery = utils.convertQueryParams.transformParamsToQuery(
830
+ RELEASE_ACTION_MODEL_UID,
831
+ query ?? {}
832
+ );
833
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
768
834
  },
769
835
  async groupActions(actions, groupBy) {
770
836
  const contentTypeUids = actions.reduce((acc, action) => {
@@ -845,10 +911,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
845
911
  return componentsMap;
846
912
  },
847
913
  async delete(releaseId) {
848
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
914
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
915
+ where: { id: releaseId },
849
916
  populate: {
850
917
  actions: {
851
- fields: ["id"]
918
+ select: ["id"]
852
919
  }
853
920
  }
854
921
  });
@@ -866,9 +933,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
866
933
  }
867
934
  }
868
935
  });
869
- await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
936
+ await strapi2.db.query(RELEASE_MODEL_UID).delete({
937
+ where: {
938
+ id: releaseId
939
+ }
940
+ });
870
941
  });
871
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
942
+ if (release2.scheduledAt) {
872
943
  const schedulingService = getService("scheduling", { strapi: strapi2 });
873
944
  await schedulingService.cancel(release2.id);
874
945
  }
@@ -876,145 +947,71 @@ const createReleaseService = ({ strapi: strapi2 }) => {
876
947
  return release2;
877
948
  },
878
949
  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) {
950
+ const {
951
+ release: release2,
952
+ error
953
+ } = await strapi2.db.transaction(async ({ trx }) => {
954
+ const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
955
+ if (!lockedRelease) {
896
956
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
897
957
  }
898
- if (releaseWithPopulatedActionEntries.releasedAt) {
958
+ if (lockedRelease.releasedAt) {
899
959
  throw new utils.errors.ValidationError("Release already published");
900
960
  }
901
- if (releaseWithPopulatedActionEntries.actions.length === 0) {
902
- throw new utils.errors.ValidationError("No entries to publish");
903
- }
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
- }
961
+ if (lockedRelease.status === "failed") {
962
+ throw new utils.errors.ValidationError("Release failed to publish");
927
963
  }
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);
964
+ try {
965
+ strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
966
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
967
+ releaseId
968
+ );
969
+ await strapi2.db.transaction(async () => {
970
+ for (const { uid, action, id } of singleTypeActions) {
971
+ await publishSingleTypeAction(uid, action, id);
974
972
  }
975
- if (entriesToUnpublish.length > 0) {
976
- await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
973
+ for (const contentTypeUid of Object.keys(collectionTypeActions)) {
974
+ const uid = contentTypeUid;
975
+ await publishCollectionTypeAction(
976
+ uid,
977
+ collectionTypeActions[uid].entriesToPublishIds,
978
+ collectionTypeActions[uid].entriesToUnpublishIds
979
+ );
977
980
  }
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
981
+ });
982
+ const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
983
+ where: {
984
+ id: releaseId
985
+ },
986
+ data: {
987
+ status: "done",
988
+ releasedAt: /* @__PURE__ */ new Date()
992
989
  }
993
- }
994
- });
995
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
990
+ });
996
991
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
997
992
  isPublished: true,
998
- release: release2
993
+ release: release22
999
994
  });
1000
- }
1001
- strapi2.telemetry.send("didPublishContentRelease");
1002
- return release2;
1003
- } catch (error) {
1004
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
995
+ strapi2.telemetry.send("didPublishContentRelease");
996
+ return { release: release22, error: null };
997
+ } catch (error2) {
1005
998
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
1006
999
  isPublished: false,
1007
- error
1000
+ error: error2
1008
1001
  });
1009
- }
1010
- strapi2.db.query(RELEASE_MODEL_UID).update({
1011
- where: { id: releaseId },
1012
- data: {
1002
+ await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
1013
1003
  status: "failed"
1014
- }
1015
- });
1004
+ }).transacting(trx).execute();
1005
+ return {
1006
+ release: null,
1007
+ error: error2
1008
+ };
1009
+ }
1010
+ });
1011
+ if (error instanceof Error) {
1016
1012
  throw error;
1017
1013
  }
1014
+ return release2;
1018
1015
  },
1019
1016
  async updateAction(actionId, releaseId, update) {
1020
1017
  const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
@@ -1101,10 +1098,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1101
1098
  }
1102
1099
  };
1103
1100
  };
1101
+ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
1102
+ constructor(message) {
1103
+ super(message);
1104
+ this.name = "AlreadyOnReleaseError";
1105
+ }
1106
+ }
1104
1107
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1105
1108
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1106
- const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
1107
- populate: { actions: { populate: { entry: { fields: ["id"] } } } }
1109
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
1110
+ where: {
1111
+ id: releaseId
1112
+ },
1113
+ populate: { actions: { populate: { entry: { select: ["id"] } } } }
1108
1114
  });
1109
1115
  if (!release2) {
1110
1116
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
@@ -1113,7 +1119,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1113
1119
  (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1114
1120
  );
1115
1121
  if (isEntryInRelease) {
1116
- throw new utils.errors.ValidationError(
1122
+ throw new AlreadyOnReleaseError(
1117
1123
  `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1118
1124
  );
1119
1125
  }
@@ -1123,17 +1129,9 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1123
1129
  if (!contentType) {
1124
1130
  throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
1125
1131
  }
1126
- if (!contentType.options?.draftAndPublish) {
1127
- throw new utils.errors.ValidationError(
1128
- `Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
1129
- );
1130
- }
1131
1132
  },
1132
1133
  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
- );
1134
+ const maximumPendingReleases = strapi2.ee.features.get("cms-content-releases")?.options?.maximumReleases || 3;
1137
1135
  const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
1138
1136
  filters: {
1139
1137
  releasedAt: {
@@ -1146,8 +1144,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1146
1144
  }
1147
1145
  },
1148
1146
  async validateUniqueNameForPendingRelease(name, id) {
1149
- const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
1150
- filters: {
1147
+ const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
1148
+ where: {
1151
1149
  releasedAt: {
1152
1150
  $null: true
1153
1151
  },
@@ -1221,7 +1219,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1221
1219
  const services = {
1222
1220
  release: createReleaseService,
1223
1221
  "release-validation": createReleaseValidationService,
1224
- ...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
1222
+ scheduling: createSchedulingService
1225
1223
  };
1226
1224
  const RELEASE_SCHEMA = yup__namespace.object().shape({
1227
1225
  name: yup__namespace.string().trim().required(),
@@ -1274,7 +1272,7 @@ const releaseController = {
1274
1272
  }
1275
1273
  };
1276
1274
  });
1277
- const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
1275
+ const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1278
1276
  where: {
1279
1277
  releasedAt: null
1280
1278
  }
@@ -1397,6 +1395,38 @@ const releaseActionController = {
1397
1395
  data: releaseAction2
1398
1396
  };
1399
1397
  },
1398
+ async createMany(ctx) {
1399
+ const releaseId = ctx.params.releaseId;
1400
+ const releaseActionsArgs = ctx.request.body;
1401
+ await Promise.all(
1402
+ releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1403
+ );
1404
+ const releaseService = getService("release", { strapi });
1405
+ const releaseActions = await strapi.db.transaction(async () => {
1406
+ const releaseActions2 = await Promise.all(
1407
+ releaseActionsArgs.map(async (releaseActionArgs) => {
1408
+ try {
1409
+ const action = await releaseService.createAction(releaseId, releaseActionArgs);
1410
+ return action;
1411
+ } catch (error) {
1412
+ if (error instanceof AlreadyOnReleaseError) {
1413
+ return null;
1414
+ }
1415
+ throw error;
1416
+ }
1417
+ })
1418
+ );
1419
+ return releaseActions2;
1420
+ });
1421
+ const newReleaseActions = releaseActions.filter((action) => action !== null);
1422
+ ctx.body = {
1423
+ data: newReleaseActions,
1424
+ meta: {
1425
+ entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1426
+ totalEntries: releaseActions.length
1427
+ }
1428
+ };
1429
+ },
1400
1430
  async findMany(ctx) {
1401
1431
  const releaseId = ctx.params.releaseId;
1402
1432
  const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
@@ -1420,7 +1450,7 @@ const releaseActionController = {
1420
1450
  acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
1421
1451
  return acc;
1422
1452
  }, {});
1423
- const sanitizedResults = await utils.mapAsync(results, async (action) => ({
1453
+ const sanitizedResults = await utils.async.map(results, async (action) => ({
1424
1454
  ...action,
1425
1455
  entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
1426
1456
  }));
@@ -1582,6 +1612,22 @@ const releaseAction = {
1582
1612
  ]
1583
1613
  }
1584
1614
  },
1615
+ {
1616
+ method: "POST",
1617
+ path: "/:releaseId/actions/bulk",
1618
+ handler: "release-action.createMany",
1619
+ config: {
1620
+ policies: [
1621
+ "admin::isAuthenticatedAdmin",
1622
+ {
1623
+ name: "admin::hasPermissions",
1624
+ config: {
1625
+ actions: ["plugin::content-releases.create-action"]
1626
+ }
1627
+ }
1628
+ ]
1629
+ }
1630
+ },
1585
1631
  {
1586
1632
  method: "GET",
1587
1633
  path: "/:releaseId/actions",
@@ -1636,9 +1682,8 @@ const routes = {
1636
1682
  release,
1637
1683
  "release-action": releaseAction
1638
1684
  };
1639
- const { features } = require("@strapi/strapi/dist/utils/ee");
1640
1685
  const getPlugin = () => {
1641
- if (features.isEnabled("cms-content-releases")) {
1686
+ if (strapi.ee.features.isEnabled("cms-content-releases")) {
1642
1687
  return {
1643
1688
  register,
1644
1689
  bootstrap,