@strapi/content-releases 0.0.0-next.56199ab7a5f3320e0debcbe4a24fe0b8cd599e21 → 0.0.0-next.6a58621932ad3d83bf9d6928c1871e7906adcd59

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.
@@ -1,4 +1,5 @@
1
- import { setCreatorFields, errors, validateYupSchema, yup as yup$1, mapAsync } from "@strapi/utils";
1
+ import { contentTypes as contentTypes$1, mapAsync, setCreatorFields, errors, validateYupSchema, yup as yup$1 } from "@strapi/utils";
2
+ import { difference, keys } from "lodash";
2
3
  import _ from "lodash/fp";
3
4
  import EE from "@strapi/strapi/dist/utils/ee";
4
5
  import * as yup from "yup";
@@ -48,40 +49,47 @@ const ACTIONS = [
48
49
  pluginName: "content-releases"
49
50
  }
50
51
  ];
51
- const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
52
- return strapi2.plugin("content-releases").service(name);
53
- };
52
+ async function deleteActionsOnDisableDraftAndPublish({
53
+ oldContentTypes,
54
+ contentTypes: contentTypes2
55
+ }) {
56
+ if (!oldContentTypes) {
57
+ return;
58
+ }
59
+ for (const uid in contentTypes2) {
60
+ if (!oldContentTypes[uid]) {
61
+ continue;
62
+ }
63
+ const oldContentType = oldContentTypes[uid];
64
+ const contentType = contentTypes2[uid];
65
+ if (contentTypes$1.hasDraftAndPublish(oldContentType) && !contentTypes$1.hasDraftAndPublish(contentType)) {
66
+ await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
67
+ }
68
+ }
69
+ }
70
+ async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
71
+ const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes2)) ?? [];
72
+ if (deletedContentTypes.length) {
73
+ await mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
74
+ return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
75
+ });
76
+ }
77
+ }
54
78
  const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
55
79
  const register = async ({ strapi: strapi2 }) => {
56
- if (features$2.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
80
+ if (features$2.isEnabled("cms-content-releases")) {
57
81
  await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
58
- const releaseActionService = getService("release-action", { strapi: strapi2 });
59
- const eventManager = getService("event-manager", { strapi: strapi2 });
60
- const destroyContentTypeUpdateListener = strapi2.eventHub.on(
61
- "content-type.update",
62
- async ({ contentType }) => {
63
- if (contentType.schema.options.draftAndPublish === false) {
64
- await releaseActionService.deleteManyForContentType(contentType.uid);
65
- }
66
- }
67
- );
68
- eventManager.addDestroyListenerCallback(destroyContentTypeUpdateListener);
69
- const destroyContentTypeDeleteListener = strapi2.eventHub.on(
70
- "content-type.delete",
71
- async ({ contentType }) => {
72
- await releaseActionService.deleteManyForContentType(contentType.uid);
73
- }
74
- );
75
- eventManager.addDestroyListenerCallback(destroyContentTypeDeleteListener);
82
+ strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
83
+ strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
76
84
  }
77
85
  };
78
86
  const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
79
87
  const bootstrap = async ({ strapi: strapi2 }) => {
80
- if (features$1.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
88
+ if (features$1.isEnabled("cms-content-releases")) {
81
89
  strapi2.db.lifecycles.subscribe({
82
90
  afterDelete(event) {
83
91
  const { model, result } = event;
84
- if (model.kind === "collectionType" && model.options.draftAndPublish) {
92
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
85
93
  const { id } = result;
86
94
  strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
87
95
  where: {
@@ -97,7 +105,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
97
105
  */
98
106
  async beforeDeleteMany(event) {
99
107
  const { model, params } = event;
100
- if (model.kind === "collectionType" && model.options.draftAndPublish) {
108
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
101
109
  const { where } = params;
102
110
  const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
103
111
  event.state.entriesToDelete = entriesToDelete;
@@ -212,15 +220,9 @@ const contentTypes = {
212
220
  release: release$1,
213
221
  "release-action": releaseAction$1
214
222
  };
215
- const createReleaseActionService = ({ strapi: strapi2 }) => ({
216
- async deleteManyForContentType(contentTypeUid) {
217
- return strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
218
- where: {
219
- target_type: contentTypeUid
220
- }
221
- });
222
- }
223
- });
223
+ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
224
+ return strapi2.plugin("content-releases").service(name);
225
+ };
224
226
  const getGroupName = (queryValue) => {
225
227
  switch (queryValue) {
226
228
  case "contentType":
@@ -236,7 +238,14 @@ const getGroupName = (queryValue) => {
236
238
  const createReleaseService = ({ strapi: strapi2 }) => ({
237
239
  async create(releaseData, { user }) {
238
240
  const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
239
- await getService("release-validation", { strapi: strapi2 }).validatePendingReleasesLimit();
241
+ const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease } = getService(
242
+ "release-validation",
243
+ { strapi: strapi2 }
244
+ );
245
+ await Promise.all([
246
+ validatePendingReleasesLimit(),
247
+ validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
248
+ ]);
240
249
  return strapi2.entityService.create(RELEASE_MODEL_UID, {
241
250
  data: releaseWithCreatorFields
242
251
  });
@@ -258,51 +267,66 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
258
267
  }
259
268
  });
260
269
  },
261
- async findManyForContentTypeEntry(contentTypeUid, entryId, {
262
- hasEntryAttached
263
- } = {
264
- hasEntryAttached: false
265
- }) {
266
- const whereActions = hasEntryAttached ? {
267
- // Find all Releases where the content type entry is present
268
- actions: {
269
- target_type: contentTypeUid,
270
- target_id: entryId
271
- }
272
- } : {
273
- // Find all Releases where the content type entry is not present
274
- $or: [
275
- {
276
- $not: {
277
- actions: {
278
- target_type: contentTypeUid,
279
- target_id: entryId
280
- }
281
- }
270
+ async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
271
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
272
+ where: {
273
+ actions: {
274
+ target_type: contentTypeUid,
275
+ target_id: entryId
282
276
  },
283
- {
284
- actions: null
277
+ releasedAt: {
278
+ $null: true
285
279
  }
286
- ]
287
- };
288
- const populateAttachedAction = hasEntryAttached ? {
289
- // Filter the action to get only the content type entry
290
- actions: {
291
- where: {
280
+ },
281
+ populate: {
282
+ // Filter the action to get only the content type entry
283
+ actions: {
284
+ where: {
285
+ target_type: contentTypeUid,
286
+ target_id: entryId
287
+ }
288
+ }
289
+ }
290
+ });
291
+ return releases.map((release2) => {
292
+ if (release2.actions?.length) {
293
+ const [actionForEntry] = release2.actions;
294
+ delete release2.actions;
295
+ return {
296
+ ...release2,
297
+ action: actionForEntry
298
+ };
299
+ }
300
+ return release2;
301
+ });
302
+ },
303
+ async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
304
+ const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
305
+ where: {
306
+ releasedAt: {
307
+ $null: true
308
+ },
309
+ actions: {
292
310
  target_type: contentTypeUid,
293
311
  target_id: entryId
294
312
  }
295
313
  }
296
- } : {};
314
+ });
297
315
  const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
298
316
  where: {
299
- ...whereActions,
317
+ $or: [
318
+ {
319
+ id: {
320
+ $notIn: releasesRelated.map((release2) => release2.id)
321
+ }
322
+ },
323
+ {
324
+ actions: null
325
+ }
326
+ ],
300
327
  releasedAt: {
301
328
  $null: true
302
329
  }
303
- },
304
- populate: {
305
- ...populateAttachedAction
306
330
  }
307
331
  });
308
332
  return releases.map((release2) => {
@@ -501,7 +525,9 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
501
525
  populate: {
502
526
  actions: {
503
527
  populate: {
504
- entry: true
528
+ entry: {
529
+ fields: ["id"]
530
+ }
505
531
  }
506
532
  }
507
533
  }
@@ -521,25 +547,49 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
521
547
  const contentTypeUid = action.contentType;
522
548
  if (!actions[contentTypeUid]) {
523
549
  actions[contentTypeUid] = {
524
- publish: [],
525
- unpublish: []
550
+ entriestoPublishIds: [],
551
+ entriesToUnpublishIds: []
526
552
  };
527
553
  }
528
554
  if (action.type === "publish") {
529
- actions[contentTypeUid].publish.push(action.entry);
555
+ actions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
530
556
  } else {
531
- actions[contentTypeUid].unpublish.push(action.entry);
557
+ actions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
532
558
  }
533
559
  }
534
560
  const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
561
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
535
562
  await strapi2.db.transaction(async () => {
536
563
  for (const contentTypeUid of Object.keys(actions)) {
537
- const { publish, unpublish } = actions[contentTypeUid];
538
- if (publish.length > 0) {
539
- await entityManagerService.publishMany(publish, contentTypeUid);
564
+ const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
565
+ const { entriestoPublishIds, entriesToUnpublishIds } = actions[contentTypeUid];
566
+ const entriesToPublish = await strapi2.entityService.findMany(
567
+ contentTypeUid,
568
+ {
569
+ filters: {
570
+ id: {
571
+ $in: entriestoPublishIds
572
+ }
573
+ },
574
+ populate
575
+ }
576
+ );
577
+ const entriesToUnpublish = await strapi2.entityService.findMany(
578
+ contentTypeUid,
579
+ {
580
+ filters: {
581
+ id: {
582
+ $in: entriesToUnpublishIds
583
+ }
584
+ },
585
+ populate
586
+ }
587
+ );
588
+ if (entriesToPublish.length > 0) {
589
+ await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
540
590
  }
541
- if (unpublish.length > 0) {
542
- await entityManagerService.unpublishMany(unpublish, contentTypeUid);
591
+ if (entriesToUnpublish.length > 0) {
592
+ await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
543
593
  }
544
594
  }
545
595
  });
@@ -637,31 +687,25 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
637
687
  if (pendingReleasesCount >= maximumPendingReleases) {
638
688
  throw new errors.ValidationError("You have reached the maximum number of pending releases");
639
689
  }
640
- }
641
- });
642
- const createEventManagerService = () => {
643
- const state = {
644
- destroyListenerCallbacks: []
645
- };
646
- return {
647
- addDestroyListenerCallback(destroyListenerCallback) {
648
- state.destroyListenerCallbacks.push(destroyListenerCallback);
649
- },
650
- destroyAllListeners() {
651
- if (!state.destroyListenerCallbacks.length) {
652
- return;
690
+ },
691
+ async validateUniqueNameForPendingRelease(name) {
692
+ const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
693
+ filters: {
694
+ releasedAt: {
695
+ $null: true
696
+ },
697
+ name
653
698
  }
654
- state.destroyListenerCallbacks.forEach((destroyListenerCallback) => {
655
- destroyListenerCallback();
656
- });
699
+ });
700
+ const isNameUnique = pendingReleases.length === 0;
701
+ if (!isNameUnique) {
702
+ throw new errors.ValidationError(`Release with name ${name} already exists`);
657
703
  }
658
- };
659
- };
704
+ }
705
+ });
660
706
  const services = {
661
707
  release: createReleaseService,
662
- "release-action": createReleaseActionService,
663
- "release-validation": createReleaseValidationService,
664
- "event-manager": createEventManagerService
708
+ "release-validation": createReleaseValidationService
665
709
  };
666
710
  const RELEASE_SCHEMA = yup.object().shape({
667
711
  name: yup.string().trim().required()
@@ -681,9 +725,7 @@ const releaseController = {
681
725
  const contentTypeUid = query.contentTypeUid;
682
726
  const entryId = query.entryId;
683
727
  const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
684
- const data = await releaseService.findManyForContentTypeEntry(contentTypeUid, entryId, {
685
- hasEntryAttached
686
- });
728
+ const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
687
729
  ctx.body = { data };
688
730
  } else {
689
731
  const query = await permissionsManager.sanitizeQuery(ctx.query);
@@ -1040,19 +1082,14 @@ const routes = {
1040
1082
  };
1041
1083
  const { features } = require("@strapi/strapi/dist/utils/ee");
1042
1084
  const getPlugin = () => {
1043
- if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
1085
+ if (features.isEnabled("cms-content-releases")) {
1044
1086
  return {
1045
1087
  register,
1046
1088
  bootstrap,
1047
1089
  contentTypes,
1048
1090
  services,
1049
1091
  controllers,
1050
- routes,
1051
- destroy() {
1052
- if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
1053
- getService("event-manager").destroyAllListeners();
1054
- }
1055
- }
1092
+ routes
1056
1093
  };
1057
1094
  }
1058
1095
  return {