@strapi/content-releases 4.20.5 → 4.21.1

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 (31) hide show
  1. package/dist/_chunks/{App-bpzO2Ljh.mjs → App-XbK-TdJn.mjs} +96 -107
  2. package/dist/_chunks/App-XbK-TdJn.mjs.map +1 -0
  3. package/dist/_chunks/{App-p8aKBitd.js → App-ftICpqDz.js} +95 -106
  4. package/dist/_chunks/App-ftICpqDz.js.map +1 -0
  5. package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-3tRbmbY3.mjs} +2 -2
  6. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
  7. package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-bpIYXOfu.js} +2 -2
  8. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
  9. package/dist/_chunks/{en-gcJJ5htG.js → en-4CUzVH2g.js} +2 -3
  10. package/dist/_chunks/en-4CUzVH2g.js.map +1 -0
  11. package/dist/_chunks/{en-WuuhP6Bn.mjs → en-pOJ6G5fC.mjs} +2 -3
  12. package/dist/_chunks/en-pOJ6G5fC.mjs.map +1 -0
  13. package/dist/_chunks/{index-AECgcaDa.mjs → index-8LrruHqK.mjs} +9 -9
  14. package/dist/_chunks/index-8LrruHqK.mjs.map +1 -0
  15. package/dist/_chunks/{index-fP3qoWZ4.js → index-RYVGXFeL.js} +9 -9
  16. package/dist/_chunks/index-RYVGXFeL.js.map +1 -0
  17. package/dist/admin/index.js +1 -1
  18. package/dist/admin/index.mjs +1 -1
  19. package/dist/server/index.js +213 -151
  20. package/dist/server/index.js.map +1 -1
  21. package/dist/server/index.mjs +213 -151
  22. package/dist/server/index.mjs.map +1 -1
  23. package/package.json +9 -9
  24. package/dist/_chunks/App-bpzO2Ljh.mjs.map +0 -1
  25. package/dist/_chunks/App-p8aKBitd.js.map +0 -1
  26. package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
  27. package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
  28. package/dist/_chunks/en-WuuhP6Bn.mjs.map +0 -1
  29. package/dist/_chunks/en-gcJJ5htG.js.map +0 -1
  30. package/dist/_chunks/index-AECgcaDa.mjs.map +0 -1
  31. package/dist/_chunks/index-fP3qoWZ4.js.map +0 -1
@@ -407,27 +407,23 @@ const bootstrap = async ({ strapi: strapi2 }) => {
407
407
  }
408
408
  }
409
409
  });
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
- }
410
+ getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
411
+ strapi2.log.error(
412
+ "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
413
+ );
414
+ throw err;
415
+ });
416
+ Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
417
+ strapi2.webhookStore.addAllowedEvent(key, value);
418
+ });
421
419
  }
422
420
  };
423
421
  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
- }
422
+ const scheduledJobs = getService("scheduling", {
423
+ strapi: strapi2
424
+ }).getAll();
425
+ for (const [, job] of scheduledJobs) {
426
+ job.cancel();
431
427
  }
432
428
  };
433
429
  const schema$1 = {
@@ -552,6 +548,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
552
548
  release: release2
553
549
  });
554
550
  };
551
+ const publishSingleTypeAction = async (uid, actionType, entryId) => {
552
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
553
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
554
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
555
+ const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
556
+ try {
557
+ if (actionType === "publish") {
558
+ await entityManagerService.publish(entry, uid);
559
+ } else {
560
+ await entityManagerService.unpublish(entry, uid);
561
+ }
562
+ } catch (error) {
563
+ if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
564
+ ;
565
+ else {
566
+ throw error;
567
+ }
568
+ }
569
+ };
570
+ const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
571
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
572
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
573
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
574
+ const entriesToPublish = await strapi2.entityService.findMany(uid, {
575
+ filters: {
576
+ id: {
577
+ $in: entriesToPublishIds
578
+ }
579
+ },
580
+ populate
581
+ });
582
+ const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
583
+ filters: {
584
+ id: {
585
+ $in: entriestoUnpublishIds
586
+ }
587
+ },
588
+ populate
589
+ });
590
+ if (entriesToPublish.length > 0) {
591
+ await entityManagerService.publishMany(entriesToPublish, uid);
592
+ }
593
+ if (entriesToUnpublish.length > 0) {
594
+ await entityManagerService.unpublishMany(entriesToUnpublish, uid);
595
+ }
596
+ };
597
+ const getFormattedActions = async (releaseId) => {
598
+ const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
599
+ where: {
600
+ release: {
601
+ id: releaseId
602
+ }
603
+ },
604
+ populate: {
605
+ entry: {
606
+ fields: ["id"]
607
+ }
608
+ }
609
+ });
610
+ if (actions.length === 0) {
611
+ throw new utils.errors.ValidationError("No entries to publish");
612
+ }
613
+ const collectionTypeActions = {};
614
+ const singleTypeActions = [];
615
+ for (const action of actions) {
616
+ const contentTypeUid = action.contentType;
617
+ if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
618
+ if (!collectionTypeActions[contentTypeUid]) {
619
+ collectionTypeActions[contentTypeUid] = {
620
+ entriesToPublishIds: [],
621
+ entriesToUnpublishIds: []
622
+ };
623
+ }
624
+ if (action.type === "publish") {
625
+ collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
626
+ } else {
627
+ collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
628
+ }
629
+ } else {
630
+ singleTypeActions.push({
631
+ uid: contentTypeUid,
632
+ action: action.type,
633
+ id: action.entry.id
634
+ });
635
+ }
636
+ }
637
+ return { collectionTypeActions, singleTypeActions };
638
+ };
555
639
  return {
556
640
  async create(releaseData, { user }) {
557
641
  const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
@@ -571,7 +655,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
571
655
  status: "empty"
572
656
  }
573
657
  });
574
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
658
+ if (releaseWithCreatorFields.scheduledAt) {
575
659
  const schedulingService = getService("scheduling", { strapi: strapi2 });
576
660
  await schedulingService.set(release2.id, release2.scheduledAt);
577
661
  }
@@ -696,13 +780,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
696
780
  // @ts-expect-error see above
697
781
  data: releaseWithCreatorFields
698
782
  });
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
- }
783
+ const schedulingService = getService("scheduling", { strapi: strapi2 });
784
+ if (releaseData.scheduledAt) {
785
+ await schedulingService.set(id, releaseData.scheduledAt);
786
+ } else if (release2.scheduledAt) {
787
+ schedulingService.cancel(id);
706
788
  }
707
789
  this.updateReleaseStatus(id);
708
790
  strapi2.telemetry.send("didUpdateContentRelease");
@@ -868,7 +950,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
868
950
  });
869
951
  await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
870
952
  });
871
- if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
953
+ if (release2.scheduledAt) {
872
954
  const schedulingService = getService("scheduling", { strapi: strapi2 });
873
955
  await schedulingService.cancel(release2.id);
874
956
  }
@@ -876,145 +958,71 @@ const createReleaseService = ({ strapi: strapi2 }) => {
876
958
  return release2;
877
959
  },
878
960
  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) {
961
+ const {
962
+ release: release2,
963
+ error
964
+ } = await strapi2.db.transaction(async ({ trx }) => {
965
+ const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
966
+ if (!lockedRelease) {
896
967
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
897
968
  }
898
- if (releaseWithPopulatedActionEntries.releasedAt) {
969
+ if (lockedRelease.releasedAt) {
899
970
  throw new utils.errors.ValidationError("Release already published");
900
971
  }
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
- }
972
+ if (lockedRelease.status === "failed") {
973
+ throw new utils.errors.ValidationError("Release failed to publish");
927
974
  }
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);
975
+ try {
976
+ strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
977
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
978
+ releaseId
979
+ );
980
+ await strapi2.db.transaction(async () => {
981
+ for (const { uid, action, id } of singleTypeActions) {
982
+ await publishSingleTypeAction(uid, action, id);
974
983
  }
975
- if (entriesToUnpublish.length > 0) {
976
- await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
984
+ for (const contentTypeUid of Object.keys(collectionTypeActions)) {
985
+ const uid = contentTypeUid;
986
+ await publishCollectionTypeAction(
987
+ uid,
988
+ collectionTypeActions[uid].entriesToPublishIds,
989
+ collectionTypeActions[uid].entriesToUnpublishIds
990
+ );
977
991
  }
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
992
+ });
993
+ const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
994
+ where: {
995
+ id: releaseId
996
+ },
997
+ data: {
998
+ status: "done",
999
+ releasedAt: /* @__PURE__ */ new Date()
992
1000
  }
993
- }
994
- });
995
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
1001
+ });
996
1002
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
997
1003
  isPublished: true,
998
- release: release2
1004
+ release: release22
999
1005
  });
1000
- }
1001
- strapi2.telemetry.send("didPublishContentRelease");
1002
- return release2;
1003
- } catch (error) {
1004
- if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
1006
+ strapi2.telemetry.send("didPublishContentRelease");
1007
+ return { release: release22, error: null };
1008
+ } catch (error2) {
1005
1009
  dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
1006
1010
  isPublished: false,
1007
- error
1011
+ error: error2
1008
1012
  });
1009
- }
1010
- strapi2.db.query(RELEASE_MODEL_UID).update({
1011
- where: { id: releaseId },
1012
- data: {
1013
+ await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
1013
1014
  status: "failed"
1014
- }
1015
- });
1015
+ }).transacting(trx).execute();
1016
+ return {
1017
+ release: null,
1018
+ error: error2
1019
+ };
1020
+ }
1021
+ });
1022
+ if (error) {
1016
1023
  throw error;
1017
1024
  }
1025
+ return release2;
1018
1026
  },
1019
1027
  async updateAction(actionId, releaseId, update) {
1020
1028
  const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
@@ -1101,6 +1109,12 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1101
1109
  }
1102
1110
  };
1103
1111
  };
1112
+ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
1113
+ constructor(message) {
1114
+ super(message);
1115
+ this.name = "AlreadyOnReleaseError";
1116
+ }
1117
+ }
1104
1118
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1105
1119
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1106
1120
  const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
@@ -1113,7 +1127,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1113
1127
  (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1114
1128
  );
1115
1129
  if (isEntryInRelease) {
1116
- throw new utils.errors.ValidationError(
1130
+ throw new AlreadyOnReleaseError(
1117
1131
  `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1118
1132
  );
1119
1133
  }
@@ -1221,7 +1235,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1221
1235
  const services = {
1222
1236
  release: createReleaseService,
1223
1237
  "release-validation": createReleaseValidationService,
1224
- ...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
1238
+ scheduling: createSchedulingService
1225
1239
  };
1226
1240
  const RELEASE_SCHEMA = yup__namespace.object().shape({
1227
1241
  name: yup__namespace.string().trim().required(),
@@ -1397,6 +1411,38 @@ const releaseActionController = {
1397
1411
  data: releaseAction2
1398
1412
  };
1399
1413
  },
1414
+ async createMany(ctx) {
1415
+ const releaseId = ctx.params.releaseId;
1416
+ const releaseActionsArgs = ctx.request.body;
1417
+ await Promise.all(
1418
+ releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1419
+ );
1420
+ const releaseService = getService("release", { strapi });
1421
+ const releaseActions = await strapi.db.transaction(async () => {
1422
+ const releaseActions2 = await Promise.all(
1423
+ releaseActionsArgs.map(async (releaseActionArgs) => {
1424
+ try {
1425
+ const action = await releaseService.createAction(releaseId, releaseActionArgs);
1426
+ return action;
1427
+ } catch (error) {
1428
+ if (error instanceof AlreadyOnReleaseError) {
1429
+ return null;
1430
+ }
1431
+ throw error;
1432
+ }
1433
+ })
1434
+ );
1435
+ return releaseActions2;
1436
+ });
1437
+ const newReleaseActions = releaseActions.filter((action) => action !== null);
1438
+ ctx.body = {
1439
+ data: newReleaseActions,
1440
+ meta: {
1441
+ entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1442
+ totalEntries: releaseActions.length
1443
+ }
1444
+ };
1445
+ },
1400
1446
  async findMany(ctx) {
1401
1447
  const releaseId = ctx.params.releaseId;
1402
1448
  const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
@@ -1582,6 +1628,22 @@ const releaseAction = {
1582
1628
  ]
1583
1629
  }
1584
1630
  },
1631
+ {
1632
+ method: "POST",
1633
+ path: "/:releaseId/actions/bulk",
1634
+ handler: "release-action.createMany",
1635
+ config: {
1636
+ policies: [
1637
+ "admin::isAuthenticatedAdmin",
1638
+ {
1639
+ name: "admin::hasPermissions",
1640
+ config: {
1641
+ actions: ["plugin::content-releases.create-action"]
1642
+ }
1643
+ }
1644
+ ]
1645
+ }
1646
+ },
1585
1647
  {
1586
1648
  method: "GET",
1587
1649
  path: "/:releaseId/actions",