@strapi/content-releases 0.0.0-next.a9d79bec775daaf0da4e506b2aebafdb4ca95b06 → 0.0.0-next.ac2b9fdba5ef59eb22c4e387ac1c5a13dd219f29

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 (32) hide show
  1. package/LICENSE +17 -1
  2. package/dist/_chunks/{App-p8aKBitd.js → App-dLXY5ei3.js} +138 -131
  3. package/dist/_chunks/App-dLXY5ei3.js.map +1 -0
  4. package/dist/_chunks/{App-bpzO2Ljh.mjs → App-jrh58sXY.mjs} +141 -134
  5. package/dist/_chunks/App-jrh58sXY.mjs.map +1 -0
  6. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-3tRbmbY3.mjs} +2 -2
  7. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
  8. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-bpIYXOfu.js} +2 -2
  9. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
  10. package/dist/_chunks/{en-gcJJ5htG.js → en-HrREghh3.js} +11 -3
  11. package/dist/_chunks/en-HrREghh3.js.map +1 -0
  12. package/dist/_chunks/{en-WuuhP6Bn.mjs → en-ltT1TlKQ.mjs} +11 -3
  13. package/dist/_chunks/en-ltT1TlKQ.mjs.map +1 -0
  14. package/dist/_chunks/{index-fP3qoWZ4.js → index-CVO0Rqdm.js} +320 -18
  15. package/dist/_chunks/index-CVO0Rqdm.js.map +1 -0
  16. package/dist/_chunks/{index-AECgcaDa.mjs → index-PiOGBETy.mjs} +325 -23
  17. package/dist/_chunks/index-PiOGBETy.mjs.map +1 -0
  18. package/dist/admin/index.js +1 -1
  19. package/dist/admin/index.mjs +1 -1
  20. package/dist/server/index.js +291 -160
  21. package/dist/server/index.js.map +1 -1
  22. package/dist/server/index.mjs +291 -160
  23. package/dist/server/index.mjs.map +1 -1
  24. package/package.json +15 -14
  25. package/dist/_chunks/App-bpzO2Ljh.mjs.map +0 -1
  26. package/dist/_chunks/App-p8aKBitd.js.map +0 -1
  27. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
  28. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
  29. package/dist/_chunks/en-WuuhP6Bn.mjs.map +0 -1
  30. package/dist/_chunks/en-gcJJ5htG.js.map +0 -1
  31. package/dist/_chunks/index-AECgcaDa.mjs.map +0 -1
  32. package/dist/_chunks/index-fP3qoWZ4.js.map +0 -1
@@ -235,13 +235,16 @@ async function disableContentTypeLocalized({ oldContentTypes, contentTypes: cont
235
235
  if (!oldContentTypes) {
236
236
  return;
237
237
  }
238
+ const i18nPlugin = strapi.plugin("i18n");
239
+ if (!i18nPlugin) {
240
+ return;
241
+ }
238
242
  for (const uid in contentTypes2) {
239
243
  if (!oldContentTypes[uid]) {
240
244
  continue;
241
245
  }
242
246
  const oldContentType = oldContentTypes[uid];
243
247
  const contentType = contentTypes2[uid];
244
- const i18nPlugin = strapi.plugin("i18n");
245
248
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
246
249
  if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
247
250
  await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
@@ -254,13 +257,16 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
254
257
  if (!oldContentTypes) {
255
258
  return;
256
259
  }
260
+ const i18nPlugin = strapi.plugin("i18n");
261
+ if (!i18nPlugin) {
262
+ return;
263
+ }
257
264
  for (const uid in contentTypes2) {
258
265
  if (!oldContentTypes[uid]) {
259
266
  continue;
260
267
  }
261
268
  const oldContentType = oldContentTypes[uid];
262
269
  const contentType = contentTypes2[uid];
263
- const i18nPlugin = strapi.plugin("i18n");
264
270
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
265
271
  const { getDefaultLocale } = i18nPlugin.service("locales");
266
272
  if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
@@ -407,27 +413,23 @@ const bootstrap = async ({ strapi: strapi2 }) => {
407
413
  }
408
414
  }
409
415
  });
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
- }
416
+ getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
417
+ strapi2.log.error(
418
+ "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
419
+ );
420
+ throw err;
421
+ });
422
+ Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
423
+ strapi2.webhookStore.addAllowedEvent(key, value);
424
+ });
421
425
  }
422
426
  };
423
427
  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
- }
428
+ const scheduledJobs = getService("scheduling", {
429
+ strapi: strapi2
430
+ }).getAll();
431
+ for (const [, job] of scheduledJobs) {
432
+ job.cancel();
431
433
  }
432
434
  };
433
435
  const schema$1 = {
@@ -552,6 +554,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
552
554
  release: release2
553
555
  });
554
556
  };
557
+ const publishSingleTypeAction = async (uid, actionType, entryId) => {
558
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
559
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
560
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
561
+ const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
562
+ try {
563
+ if (actionType === "publish") {
564
+ await entityManagerService.publish(entry, uid);
565
+ } else {
566
+ await entityManagerService.unpublish(entry, uid);
567
+ }
568
+ } catch (error) {
569
+ if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
570
+ ;
571
+ else {
572
+ throw error;
573
+ }
574
+ }
575
+ };
576
+ const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
577
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
578
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
579
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
580
+ const entriesToPublish = await strapi2.entityService.findMany(uid, {
581
+ filters: {
582
+ id: {
583
+ $in: entriesToPublishIds
584
+ }
585
+ },
586
+ populate
587
+ });
588
+ const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
589
+ filters: {
590
+ id: {
591
+ $in: entriestoUnpublishIds
592
+ }
593
+ },
594
+ populate
595
+ });
596
+ if (entriesToPublish.length > 0) {
597
+ await entityManagerService.publishMany(entriesToPublish, uid);
598
+ }
599
+ if (entriesToUnpublish.length > 0) {
600
+ await entityManagerService.unpublishMany(entriesToUnpublish, uid);
601
+ }
602
+ };
603
+ const getFormattedActions = async (releaseId) => {
604
+ const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
605
+ where: {
606
+ release: {
607
+ id: releaseId
608
+ }
609
+ },
610
+ populate: {
611
+ entry: {
612
+ fields: ["id"]
613
+ }
614
+ }
615
+ });
616
+ if (actions.length === 0) {
617
+ throw new utils.errors.ValidationError("No entries to publish");
618
+ }
619
+ const collectionTypeActions = {};
620
+ const singleTypeActions = [];
621
+ for (const action of actions) {
622
+ const contentTypeUid = action.contentType;
623
+ if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
624
+ if (!collectionTypeActions[contentTypeUid]) {
625
+ collectionTypeActions[contentTypeUid] = {
626
+ entriesToPublishIds: [],
627
+ entriesToUnpublishIds: []
628
+ };
629
+ }
630
+ if (action.type === "publish") {
631
+ collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
632
+ } else {
633
+ collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
634
+ }
635
+ } else {
636
+ singleTypeActions.push({
637
+ uid: contentTypeUid,
638
+ action: action.type,
639
+ id: action.entry.id
640
+ });
641
+ }
642
+ }
643
+ return { collectionTypeActions, singleTypeActions };
644
+ };
555
645
  return {
556
646
  async create(releaseData, { user }) {
557
647
  const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
@@ -571,7 +661,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
571
661
  status: "empty"
572
662
  }
573
663
  });
574
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
664
+ if (releaseWithCreatorFields.scheduledAt) {
575
665
  const schedulingService = getService("scheduling", { strapi: strapi2 });
576
666
  await schedulingService.set(release2.id, release2.scheduledAt);
577
667
  }
@@ -595,12 +685,18 @@ const createReleaseService = ({ strapi: strapi2 }) => {
595
685
  }
596
686
  });
597
687
  },
598
- async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
688
+ async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
689
+ let entries = entriesIds;
690
+ if (!Array.isArray(entriesIds)) {
691
+ entries = [entriesIds];
692
+ }
599
693
  const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
600
694
  where: {
601
695
  actions: {
602
696
  target_type: contentTypeUid,
603
- target_id: entryId
697
+ target_id: {
698
+ $in: entries
699
+ }
604
700
  },
605
701
  releasedAt: {
606
702
  $null: true
@@ -611,18 +707,25 @@ const createReleaseService = ({ strapi: strapi2 }) => {
611
707
  actions: {
612
708
  where: {
613
709
  target_type: contentTypeUid,
614
- target_id: entryId
710
+ target_id: {
711
+ $in: entries
712
+ }
713
+ },
714
+ populate: {
715
+ entry: {
716
+ select: ["id"]
717
+ }
615
718
  }
616
719
  }
617
720
  }
618
721
  });
619
722
  return releases.map((release2) => {
620
723
  if (release2.actions?.length) {
621
- const [actionForEntry] = release2.actions;
724
+ const actionsForEntry = release2.actions;
622
725
  delete release2.actions;
623
726
  return {
624
727
  ...release2,
625
- action: actionForEntry
728
+ actions: actionsForEntry
626
729
  };
627
730
  }
628
731
  return release2;
@@ -696,19 +799,17 @@ const createReleaseService = ({ strapi: strapi2 }) => {
696
799
  // @ts-expect-error see above
697
800
  data: releaseWithCreatorFields
698
801
  });
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
- }
802
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
803
+ if (releaseData.scheduledAt) {
804
+ await schedulingService.set(id, releaseData.scheduledAt);
805
+ } else if (release2.scheduledAt) {
806
+ schedulingService.cancel(id);
706
807
  }
707
808
  this.updateReleaseStatus(id);
708
809
  strapi2.telemetry.send("didUpdateContentRelease");
709
810
  return updatedRelease;
710
811
  },
711
- async createAction(releaseId, action) {
812
+ async createAction(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
712
813
  const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
713
814
  strapi: strapi2
714
815
  });
@@ -741,7 +842,9 @@ const createReleaseService = ({ strapi: strapi2 }) => {
741
842
  },
742
843
  populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
743
844
  });
744
- this.updateReleaseStatus(releaseId);
845
+ if (!disableUpdateReleaseStatus) {
846
+ this.updateReleaseStatus(releaseId);
847
+ }
745
848
  return releaseAction2;
746
849
  },
747
850
  async findActions(releaseId, query) {
@@ -868,7 +971,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
868
971
  });
869
972
  await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
870
973
  });
871
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
974
+ if (release2.scheduledAt) {
872
975
  const schedulingService = getService("scheduling", { strapi: strapi2 });
873
976
  await schedulingService.cancel(release2.id);
874
977
  }
@@ -876,145 +979,71 @@ const createReleaseService = ({ strapi: strapi2 }) => {
876
979
  return release2;
877
980
  },
878
981
  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) {
982
+ const {
983
+ release: release2,
984
+ error
985
+ } = await strapi2.db.transaction(async ({ trx }) => {
986
+ const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
987
+ if (!lockedRelease) {
896
988
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
897
989
  }
898
- if (releaseWithPopulatedActionEntries.releasedAt) {
990
+ if (lockedRelease.releasedAt) {
899
991
  throw new utils.errors.ValidationError("Release already published");
900
992
  }
901
- if (releaseWithPopulatedActionEntries.actions.length === 0) {
902
- throw new utils.errors.ValidationError("No entries to publish");
993
+ if (lockedRelease.status === "failed") {
994
+ throw new utils.errors.ValidationError("Release failed to publish");
903
995
  }
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);
996
+ try {
997
+ strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
998
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
999
+ releaseId
1000
+ );
1001
+ await strapi2.db.transaction(async () => {
1002
+ for (const { uid, action, id } of singleTypeActions) {
1003
+ await publishSingleTypeAction(uid, action, id);
974
1004
  }
975
- if (entriesToUnpublish.length > 0) {
976
- await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
1005
+ for (const contentTypeUid of Object.keys(collectionTypeActions)) {
1006
+ const uid = contentTypeUid;
1007
+ await publishCollectionTypeAction(
1008
+ uid,
1009
+ collectionTypeActions[uid].entriesToPublishIds,
1010
+ collectionTypeActions[uid].entriesToUnpublishIds
1011
+ );
977
1012
  }
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
1013
+ });
1014
+ const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
1015
+ where: {
1016
+ id: releaseId
1017
+ },
1018
+ data: {
1019
+ status: "done",
1020
+ releasedAt: /* @__PURE__ */ new Date()
992
1021
  }
993
- }
994
- });
995
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
1022
+ });
996
1023
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
997
1024
  isPublished: true,
998
- release: release2
1025
+ release: release22
999
1026
  });
1000
- }
1001
- strapi2.telemetry.send("didPublishContentRelease");
1002
- return release2;
1003
- } catch (error) {
1004
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
1027
+ strapi2.telemetry.send("didPublishContentRelease");
1028
+ return { release: release22, error: null };
1029
+ } catch (error2) {
1005
1030
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
1006
1031
  isPublished: false,
1007
- error
1032
+ error: error2
1008
1033
  });
1009
- }
1010
- strapi2.db.query(RELEASE_MODEL_UID).update({
1011
- where: { id: releaseId },
1012
- data: {
1034
+ await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
1013
1035
  status: "failed"
1014
- }
1015
- });
1036
+ }).transacting(trx).execute();
1037
+ return {
1038
+ release: null,
1039
+ error: error2
1040
+ };
1041
+ }
1042
+ });
1043
+ if (error) {
1016
1044
  throw error;
1017
1045
  }
1046
+ return release2;
1018
1047
  },
1019
1048
  async updateAction(actionId, releaseId, update) {
1020
1049
  const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
@@ -1101,6 +1130,12 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1101
1130
  }
1102
1131
  };
1103
1132
  };
1133
+ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
1134
+ constructor(message) {
1135
+ super(message);
1136
+ this.name = "AlreadyOnReleaseError";
1137
+ }
1138
+ }
1104
1139
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1105
1140
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1106
1141
  const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
@@ -1113,7 +1148,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1113
1148
  (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1114
1149
  );
1115
1150
  if (isEntryInRelease) {
1116
- throw new utils.errors.ValidationError(
1151
+ throw new AlreadyOnReleaseError(
1117
1152
  `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1118
1153
  );
1119
1154
  }
@@ -1221,7 +1256,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1221
1256
  const services = {
1222
1257
  release: createReleaseService,
1223
1258
  "release-validation": createReleaseValidationService,
1224
- ...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
1259
+ scheduling: createSchedulingService
1225
1260
  };
1226
1261
  const RELEASE_SCHEMA = yup__namespace.object().shape({
1227
1262
  name: yup__namespace.string().trim().required(),
@@ -1308,6 +1343,33 @@ const releaseController = {
1308
1343
  };
1309
1344
  ctx.body = { data };
1310
1345
  },
1346
+ async mapEntriesToReleases(ctx) {
1347
+ const { contentTypeUid, entriesIds } = ctx.query;
1348
+ if (!contentTypeUid || !entriesIds) {
1349
+ throw new utils.errors.ValidationError("Missing required query parameters");
1350
+ }
1351
+ const releaseService = getService("release", { strapi });
1352
+ const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1353
+ contentTypeUid,
1354
+ entriesIds
1355
+ );
1356
+ const mappedEntriesInReleases = releasesWithActions.reduce(
1357
+ (acc, release2) => {
1358
+ release2.actions.forEach((action) => {
1359
+ if (!acc[action.entry.id]) {
1360
+ acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
1361
+ } else {
1362
+ acc[action.entry.id].push({ id: release2.id, name: release2.name });
1363
+ }
1364
+ });
1365
+ return acc;
1366
+ },
1367
+ {}
1368
+ );
1369
+ ctx.body = {
1370
+ data: mappedEntriesInReleases
1371
+ };
1372
+ },
1311
1373
  async create(ctx) {
1312
1374
  const user = ctx.state.user;
1313
1375
  const releaseArgs = ctx.request.body;
@@ -1397,6 +1459,43 @@ const releaseActionController = {
1397
1459
  data: releaseAction2
1398
1460
  };
1399
1461
  },
1462
+ async createMany(ctx) {
1463
+ const releaseId = ctx.params.releaseId;
1464
+ const releaseActionsArgs = ctx.request.body;
1465
+ await Promise.all(
1466
+ releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1467
+ );
1468
+ const releaseService = getService("release", { strapi });
1469
+ const releaseActions = await strapi.db.transaction(async () => {
1470
+ const releaseActions2 = await Promise.all(
1471
+ releaseActionsArgs.map(async (releaseActionArgs) => {
1472
+ try {
1473
+ const action = await releaseService.createAction(releaseId, releaseActionArgs, {
1474
+ disableUpdateReleaseStatus: true
1475
+ });
1476
+ return action;
1477
+ } catch (error) {
1478
+ if (error instanceof AlreadyOnReleaseError) {
1479
+ return null;
1480
+ }
1481
+ throw error;
1482
+ }
1483
+ })
1484
+ );
1485
+ return releaseActions2;
1486
+ });
1487
+ const newReleaseActions = releaseActions.filter((action) => action !== null);
1488
+ if (newReleaseActions.length > 0) {
1489
+ releaseService.updateReleaseStatus(releaseId);
1490
+ }
1491
+ ctx.body = {
1492
+ data: newReleaseActions,
1493
+ meta: {
1494
+ entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1495
+ totalEntries: releaseActions.length
1496
+ }
1497
+ };
1498
+ },
1400
1499
  async findMany(ctx) {
1401
1500
  const releaseId = ctx.params.releaseId;
1402
1501
  const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
@@ -1465,6 +1564,22 @@ const controllers = { release: releaseController, "release-action": releaseActio
1465
1564
  const release = {
1466
1565
  type: "admin",
1467
1566
  routes: [
1567
+ {
1568
+ method: "GET",
1569
+ path: "/mapEntriesToReleases",
1570
+ handler: "release.mapEntriesToReleases",
1571
+ config: {
1572
+ policies: [
1573
+ "admin::isAuthenticatedAdmin",
1574
+ {
1575
+ name: "admin::hasPermissions",
1576
+ config: {
1577
+ actions: ["plugin::content-releases.read"]
1578
+ }
1579
+ }
1580
+ ]
1581
+ }
1582
+ },
1468
1583
  {
1469
1584
  method: "POST",
1470
1585
  path: "/",
@@ -1582,6 +1697,22 @@ const releaseAction = {
1582
1697
  ]
1583
1698
  }
1584
1699
  },
1700
+ {
1701
+ method: "POST",
1702
+ path: "/:releaseId/actions/bulk",
1703
+ handler: "release-action.createMany",
1704
+ config: {
1705
+ policies: [
1706
+ "admin::isAuthenticatedAdmin",
1707
+ {
1708
+ name: "admin::hasPermissions",
1709
+ config: {
1710
+ actions: ["plugin::content-releases.create-action"]
1711
+ }
1712
+ }
1713
+ ]
1714
+ }
1715
+ },
1585
1716
  {
1586
1717
  method: "GET",
1587
1718
  path: "/:releaseId/actions",