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

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 +282 -158
  21. package/dist/server/index.js.map +1 -1
  22. package/dist/server/index.mjs +282 -158
  23. package/dist/server/index.mjs.map +1 -1
  24. package/package.json +13 -13
  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
@@ -211,13 +211,16 @@ async function disableContentTypeLocalized({ oldContentTypes, contentTypes: cont
211
211
  if (!oldContentTypes) {
212
212
  return;
213
213
  }
214
+ const i18nPlugin = strapi.plugin("i18n");
215
+ if (!i18nPlugin) {
216
+ return;
217
+ }
214
218
  for (const uid in contentTypes2) {
215
219
  if (!oldContentTypes[uid]) {
216
220
  continue;
217
221
  }
218
222
  const oldContentType = oldContentTypes[uid];
219
223
  const contentType = contentTypes2[uid];
220
- const i18nPlugin = strapi.plugin("i18n");
221
224
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
222
225
  if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
223
226
  await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
@@ -230,13 +233,16 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
230
233
  if (!oldContentTypes) {
231
234
  return;
232
235
  }
236
+ const i18nPlugin = strapi.plugin("i18n");
237
+ if (!i18nPlugin) {
238
+ return;
239
+ }
233
240
  for (const uid in contentTypes2) {
234
241
  if (!oldContentTypes[uid]) {
235
242
  continue;
236
243
  }
237
244
  const oldContentType = oldContentTypes[uid];
238
245
  const contentType = contentTypes2[uid];
239
- const i18nPlugin = strapi.plugin("i18n");
240
246
  const { isLocalizedContentType } = i18nPlugin.service("content-types");
241
247
  const { getDefaultLocale } = i18nPlugin.service("locales");
242
248
  if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
@@ -383,27 +389,23 @@ const bootstrap = async ({ strapi: strapi2 }) => {
383
389
  }
384
390
  }
385
391
  });
386
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
387
- getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
388
- strapi2.log.error(
389
- "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
390
- );
391
- throw err;
392
- });
393
- Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
394
- strapi2.webhookStore.addAllowedEvent(key, value);
395
- });
396
- }
392
+ getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
393
+ strapi2.log.error(
394
+ "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
395
+ );
396
+ throw err;
397
+ });
398
+ Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
399
+ strapi2.webhookStore.addAllowedEvent(key, value);
400
+ });
397
401
  }
398
402
  };
399
403
  const destroy = async ({ strapi: strapi2 }) => {
400
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
401
- const scheduledJobs = getService("scheduling", {
402
- strapi: strapi2
403
- }).getAll();
404
- for (const [, job] of scheduledJobs) {
405
- job.cancel();
406
- }
404
+ const scheduledJobs = getService("scheduling", {
405
+ strapi: strapi2
406
+ }).getAll();
407
+ for (const [, job] of scheduledJobs) {
408
+ job.cancel();
407
409
  }
408
410
  };
409
411
  const schema$1 = {
@@ -528,6 +530,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
528
530
  release: release2
529
531
  });
530
532
  };
533
+ const publishSingleTypeAction = async (uid, actionType, entryId) => {
534
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
535
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
536
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
537
+ const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
538
+ try {
539
+ if (actionType === "publish") {
540
+ await entityManagerService.publish(entry, uid);
541
+ } else {
542
+ await entityManagerService.unpublish(entry, uid);
543
+ }
544
+ } catch (error) {
545
+ if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
546
+ ;
547
+ else {
548
+ throw error;
549
+ }
550
+ }
551
+ };
552
+ const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
553
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
554
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
555
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
556
+ const entriesToPublish = await strapi2.entityService.findMany(uid, {
557
+ filters: {
558
+ id: {
559
+ $in: entriesToPublishIds
560
+ }
561
+ },
562
+ populate
563
+ });
564
+ const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
565
+ filters: {
566
+ id: {
567
+ $in: entriestoUnpublishIds
568
+ }
569
+ },
570
+ populate
571
+ });
572
+ if (entriesToPublish.length > 0) {
573
+ await entityManagerService.publishMany(entriesToPublish, uid);
574
+ }
575
+ if (entriesToUnpublish.length > 0) {
576
+ await entityManagerService.unpublishMany(entriesToUnpublish, uid);
577
+ }
578
+ };
579
+ const getFormattedActions = async (releaseId) => {
580
+ const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
581
+ where: {
582
+ release: {
583
+ id: releaseId
584
+ }
585
+ },
586
+ populate: {
587
+ entry: {
588
+ fields: ["id"]
589
+ }
590
+ }
591
+ });
592
+ if (actions.length === 0) {
593
+ throw new errors.ValidationError("No entries to publish");
594
+ }
595
+ const collectionTypeActions = {};
596
+ const singleTypeActions = [];
597
+ for (const action of actions) {
598
+ const contentTypeUid = action.contentType;
599
+ if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
600
+ if (!collectionTypeActions[contentTypeUid]) {
601
+ collectionTypeActions[contentTypeUid] = {
602
+ entriesToPublishIds: [],
603
+ entriesToUnpublishIds: []
604
+ };
605
+ }
606
+ if (action.type === "publish") {
607
+ collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
608
+ } else {
609
+ collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
610
+ }
611
+ } else {
612
+ singleTypeActions.push({
613
+ uid: contentTypeUid,
614
+ action: action.type,
615
+ id: action.entry.id
616
+ });
617
+ }
618
+ }
619
+ return { collectionTypeActions, singleTypeActions };
620
+ };
531
621
  return {
532
622
  async create(releaseData, { user }) {
533
623
  const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
@@ -547,7 +637,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
547
637
  status: "empty"
548
638
  }
549
639
  });
550
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
640
+ if (releaseWithCreatorFields.scheduledAt) {
551
641
  const schedulingService = getService("scheduling", { strapi: strapi2 });
552
642
  await schedulingService.set(release2.id, release2.scheduledAt);
553
643
  }
@@ -571,12 +661,18 @@ const createReleaseService = ({ strapi: strapi2 }) => {
571
661
  }
572
662
  });
573
663
  },
574
- async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
664
+ async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
665
+ let entries = entriesIds;
666
+ if (!Array.isArray(entriesIds)) {
667
+ entries = [entriesIds];
668
+ }
575
669
  const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
576
670
  where: {
577
671
  actions: {
578
672
  target_type: contentTypeUid,
579
- target_id: entryId
673
+ target_id: {
674
+ $in: entries
675
+ }
580
676
  },
581
677
  releasedAt: {
582
678
  $null: true
@@ -587,18 +683,25 @@ const createReleaseService = ({ strapi: strapi2 }) => {
587
683
  actions: {
588
684
  where: {
589
685
  target_type: contentTypeUid,
590
- target_id: entryId
686
+ target_id: {
687
+ $in: entries
688
+ }
689
+ },
690
+ populate: {
691
+ entry: {
692
+ select: ["id"]
693
+ }
591
694
  }
592
695
  }
593
696
  }
594
697
  });
595
698
  return releases.map((release2) => {
596
699
  if (release2.actions?.length) {
597
- const [actionForEntry] = release2.actions;
700
+ const actionsForEntry = release2.actions;
598
701
  delete release2.actions;
599
702
  return {
600
703
  ...release2,
601
- action: actionForEntry
704
+ actions: actionsForEntry
602
705
  };
603
706
  }
604
707
  return release2;
@@ -672,13 +775,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
672
775
  // @ts-expect-error see above
673
776
  data: releaseWithCreatorFields
674
777
  });
675
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
676
- const schedulingService = getService("scheduling", { strapi: strapi2 });
677
- if (releaseData.scheduledAt) {
678
- await schedulingService.set(id, releaseData.scheduledAt);
679
- } else if (release2.scheduledAt) {
680
- schedulingService.cancel(id);
681
- }
778
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
779
+ if (releaseData.scheduledAt) {
780
+ await schedulingService.set(id, releaseData.scheduledAt);
781
+ } else if (release2.scheduledAt) {
782
+ schedulingService.cancel(id);
682
783
  }
683
784
  this.updateReleaseStatus(id);
684
785
  strapi2.telemetry.send("didUpdateContentRelease");
@@ -844,7 +945,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
844
945
  });
845
946
  await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
846
947
  });
847
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
948
+ if (release2.scheduledAt) {
848
949
  const schedulingService = getService("scheduling", { strapi: strapi2 });
849
950
  await schedulingService.cancel(release2.id);
850
951
  }
@@ -852,145 +953,71 @@ const createReleaseService = ({ strapi: strapi2 }) => {
852
953
  return release2;
853
954
  },
854
955
  async publish(releaseId) {
855
- try {
856
- const releaseWithPopulatedActionEntries = await strapi2.entityService.findOne(
857
- RELEASE_MODEL_UID,
858
- releaseId,
859
- {
860
- populate: {
861
- actions: {
862
- populate: {
863
- entry: {
864
- fields: ["id"]
865
- }
866
- }
867
- }
868
- }
869
- }
870
- );
871
- if (!releaseWithPopulatedActionEntries) {
956
+ const {
957
+ release: release2,
958
+ error
959
+ } = await strapi2.db.transaction(async ({ trx }) => {
960
+ const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
961
+ if (!lockedRelease) {
872
962
  throw new errors.NotFoundError(`No release found for id ${releaseId}`);
873
963
  }
874
- if (releaseWithPopulatedActionEntries.releasedAt) {
964
+ if (lockedRelease.releasedAt) {
875
965
  throw new errors.ValidationError("Release already published");
876
966
  }
877
- if (releaseWithPopulatedActionEntries.actions.length === 0) {
878
- throw new errors.ValidationError("No entries to publish");
967
+ if (lockedRelease.status === "failed") {
968
+ throw new errors.ValidationError("Release failed to publish");
879
969
  }
880
- const collectionTypeActions = {};
881
- const singleTypeActions = [];
882
- for (const action of releaseWithPopulatedActionEntries.actions) {
883
- const contentTypeUid = action.contentType;
884
- if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
885
- if (!collectionTypeActions[contentTypeUid]) {
886
- collectionTypeActions[contentTypeUid] = {
887
- entriestoPublishIds: [],
888
- entriesToUnpublishIds: []
889
- };
890
- }
891
- if (action.type === "publish") {
892
- collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
893
- } else {
894
- collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
895
- }
896
- } else {
897
- singleTypeActions.push({
898
- uid: contentTypeUid,
899
- action: action.type,
900
- id: action.entry.id
901
- });
902
- }
903
- }
904
- const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
905
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
906
- await strapi2.db.transaction(async () => {
907
- for (const { uid, action, id } of singleTypeActions) {
908
- const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
909
- const entry = await strapi2.entityService.findOne(uid, id, { populate });
910
- try {
911
- if (action === "publish") {
912
- await entityManagerService.publish(entry, uid);
913
- } else {
914
- await entityManagerService.unpublish(entry, uid);
915
- }
916
- } catch (error) {
917
- if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
918
- } else {
919
- throw error;
920
- }
921
- }
922
- }
923
- for (const contentTypeUid of Object.keys(collectionTypeActions)) {
924
- const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
925
- const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
926
- const entriesToPublish = await strapi2.entityService.findMany(
927
- contentTypeUid,
928
- {
929
- filters: {
930
- id: {
931
- $in: entriestoPublishIds
932
- }
933
- },
934
- populate
935
- }
936
- );
937
- const entriesToUnpublish = await strapi2.entityService.findMany(
938
- contentTypeUid,
939
- {
940
- filters: {
941
- id: {
942
- $in: entriesToUnpublishIds
943
- }
944
- },
945
- populate
946
- }
947
- );
948
- if (entriesToPublish.length > 0) {
949
- await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
970
+ try {
971
+ strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
972
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
973
+ releaseId
974
+ );
975
+ await strapi2.db.transaction(async () => {
976
+ for (const { uid, action, id } of singleTypeActions) {
977
+ await publishSingleTypeAction(uid, action, id);
950
978
  }
951
- if (entriesToUnpublish.length > 0) {
952
- await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
979
+ for (const contentTypeUid of Object.keys(collectionTypeActions)) {
980
+ const uid = contentTypeUid;
981
+ await publishCollectionTypeAction(
982
+ uid,
983
+ collectionTypeActions[uid].entriesToPublishIds,
984
+ collectionTypeActions[uid].entriesToUnpublishIds
985
+ );
953
986
  }
954
- }
955
- });
956
- const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
957
- data: {
958
- /*
959
- * The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
960
- */
961
- // @ts-expect-error see above
962
- releasedAt: /* @__PURE__ */ new Date()
963
- },
964
- populate: {
965
- actions: {
966
- // @ts-expect-error is not expecting count but it is working
967
- count: true
987
+ });
988
+ const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
989
+ where: {
990
+ id: releaseId
991
+ },
992
+ data: {
993
+ status: "done",
994
+ releasedAt: /* @__PURE__ */ new Date()
968
995
  }
969
- }
970
- });
971
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
996
+ });
972
997
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
973
998
  isPublished: true,
974
- release: release2
999
+ release: release22
975
1000
  });
976
- }
977
- strapi2.telemetry.send("didPublishContentRelease");
978
- return release2;
979
- } catch (error) {
980
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
1001
+ strapi2.telemetry.send("didPublishContentRelease");
1002
+ return { release: release22, error: null };
1003
+ } catch (error2) {
981
1004
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
982
1005
  isPublished: false,
983
- error
1006
+ error: error2
984
1007
  });
985
- }
986
- strapi2.db.query(RELEASE_MODEL_UID).update({
987
- where: { id: releaseId },
988
- data: {
1008
+ await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
989
1009
  status: "failed"
990
- }
991
- });
1010
+ }).transacting(trx).execute();
1011
+ return {
1012
+ release: null,
1013
+ error: error2
1014
+ };
1015
+ }
1016
+ });
1017
+ if (error) {
992
1018
  throw error;
993
1019
  }
1020
+ return release2;
994
1021
  },
995
1022
  async updateAction(actionId, releaseId, update) {
996
1023
  const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
@@ -1077,6 +1104,12 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1077
1104
  }
1078
1105
  };
1079
1106
  };
1107
+ class AlreadyOnReleaseError extends errors.ApplicationError {
1108
+ constructor(message) {
1109
+ super(message);
1110
+ this.name = "AlreadyOnReleaseError";
1111
+ }
1112
+ }
1080
1113
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1081
1114
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1082
1115
  const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
@@ -1089,7 +1122,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1089
1122
  (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1090
1123
  );
1091
1124
  if (isEntryInRelease) {
1092
- throw new errors.ValidationError(
1125
+ throw new AlreadyOnReleaseError(
1093
1126
  `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1094
1127
  );
1095
1128
  }
@@ -1197,7 +1230,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1197
1230
  const services = {
1198
1231
  release: createReleaseService,
1199
1232
  "release-validation": createReleaseValidationService,
1200
- ...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
1233
+ scheduling: createSchedulingService
1201
1234
  };
1202
1235
  const RELEASE_SCHEMA = yup.object().shape({
1203
1236
  name: yup.string().trim().required(),
@@ -1284,6 +1317,33 @@ const releaseController = {
1284
1317
  };
1285
1318
  ctx.body = { data };
1286
1319
  },
1320
+ async mapEntriesToReleases(ctx) {
1321
+ const { contentTypeUid, entriesIds } = ctx.query;
1322
+ if (!contentTypeUid || !entriesIds) {
1323
+ throw new errors.ValidationError("Missing required query parameters");
1324
+ }
1325
+ const releaseService = getService("release", { strapi });
1326
+ const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1327
+ contentTypeUid,
1328
+ entriesIds
1329
+ );
1330
+ const mappedEntriesInReleases = releasesWithActions.reduce(
1331
+ (acc, release2) => {
1332
+ release2.actions.forEach((action) => {
1333
+ if (!acc[action.entry.id]) {
1334
+ acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
1335
+ } else {
1336
+ acc[action.entry.id].push({ id: release2.id, name: release2.name });
1337
+ }
1338
+ });
1339
+ return acc;
1340
+ },
1341
+ {}
1342
+ );
1343
+ ctx.body = {
1344
+ data: mappedEntriesInReleases
1345
+ };
1346
+ },
1287
1347
  async create(ctx) {
1288
1348
  const user = ctx.state.user;
1289
1349
  const releaseArgs = ctx.request.body;
@@ -1373,6 +1433,38 @@ const releaseActionController = {
1373
1433
  data: releaseAction2
1374
1434
  };
1375
1435
  },
1436
+ async createMany(ctx) {
1437
+ const releaseId = ctx.params.releaseId;
1438
+ const releaseActionsArgs = ctx.request.body;
1439
+ await Promise.all(
1440
+ releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1441
+ );
1442
+ const releaseService = getService("release", { strapi });
1443
+ const releaseActions = await strapi.db.transaction(async () => {
1444
+ const releaseActions2 = await Promise.all(
1445
+ releaseActionsArgs.map(async (releaseActionArgs) => {
1446
+ try {
1447
+ const action = await releaseService.createAction(releaseId, releaseActionArgs);
1448
+ return action;
1449
+ } catch (error) {
1450
+ if (error instanceof AlreadyOnReleaseError) {
1451
+ return null;
1452
+ }
1453
+ throw error;
1454
+ }
1455
+ })
1456
+ );
1457
+ return releaseActions2;
1458
+ });
1459
+ const newReleaseActions = releaseActions.filter((action) => action !== null);
1460
+ ctx.body = {
1461
+ data: newReleaseActions,
1462
+ meta: {
1463
+ entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1464
+ totalEntries: releaseActions.length
1465
+ }
1466
+ };
1467
+ },
1376
1468
  async findMany(ctx) {
1377
1469
  const releaseId = ctx.params.releaseId;
1378
1470
  const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
@@ -1441,6 +1533,22 @@ const controllers = { release: releaseController, "release-action": releaseActio
1441
1533
  const release = {
1442
1534
  type: "admin",
1443
1535
  routes: [
1536
+ {
1537
+ method: "GET",
1538
+ path: "/mapEntriesToReleases",
1539
+ handler: "release.mapEntriesToReleases",
1540
+ config: {
1541
+ policies: [
1542
+ "admin::isAuthenticatedAdmin",
1543
+ {
1544
+ name: "admin::hasPermissions",
1545
+ config: {
1546
+ actions: ["plugin::content-releases.read"]
1547
+ }
1548
+ }
1549
+ ]
1550
+ }
1551
+ },
1444
1552
  {
1445
1553
  method: "POST",
1446
1554
  path: "/",
@@ -1558,6 +1666,22 @@ const releaseAction = {
1558
1666
  ]
1559
1667
  }
1560
1668
  },
1669
+ {
1670
+ method: "POST",
1671
+ path: "/:releaseId/actions/bulk",
1672
+ handler: "release-action.createMany",
1673
+ config: {
1674
+ policies: [
1675
+ "admin::isAuthenticatedAdmin",
1676
+ {
1677
+ name: "admin::hasPermissions",
1678
+ config: {
1679
+ actions: ["plugin::content-releases.create-action"]
1680
+ }
1681
+ }
1682
+ ]
1683
+ }
1684
+ },
1561
1685
  {
1562
1686
  method: "GET",
1563
1687
  path: "/:releaseId/actions",