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

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,5 +1,6 @@
1
1
  "use strict";
2
2
  const utils = require("@strapi/utils");
3
+ const lodash = require("lodash");
3
4
  const _ = require("lodash/fp");
4
5
  const EE = require("@strapi/strapi/dist/utils/ee");
5
6
  const yup = require("yup");
@@ -71,40 +72,47 @@ const ACTIONS = [
71
72
  pluginName: "content-releases"
72
73
  }
73
74
  ];
74
- const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
75
- return strapi2.plugin("content-releases").service(name);
76
- };
75
+ async function deleteActionsOnDisableDraftAndPublish({
76
+ oldContentTypes,
77
+ contentTypes: contentTypes2
78
+ }) {
79
+ if (!oldContentTypes) {
80
+ return;
81
+ }
82
+ for (const uid in contentTypes2) {
83
+ if (!oldContentTypes[uid]) {
84
+ continue;
85
+ }
86
+ const oldContentType = oldContentTypes[uid];
87
+ const contentType = contentTypes2[uid];
88
+ if (utils.contentTypes.hasDraftAndPublish(oldContentType) && !utils.contentTypes.hasDraftAndPublish(contentType)) {
89
+ await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
90
+ }
91
+ }
92
+ }
93
+ async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
94
+ const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
95
+ if (deletedContentTypes.length) {
96
+ await utils.mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
97
+ return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
98
+ });
99
+ }
100
+ }
77
101
  const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
78
102
  const register = async ({ strapi: strapi2 }) => {
79
- if (features$2.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
103
+ if (features$2.isEnabled("cms-content-releases")) {
80
104
  await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
81
- const releaseActionService = getService("release-action", { strapi: strapi2 });
82
- const eventManager = getService("event-manager", { strapi: strapi2 });
83
- const destroyContentTypeUpdateListener = strapi2.eventHub.on(
84
- "content-type.update",
85
- async ({ contentType }) => {
86
- if (contentType.schema.options.draftAndPublish === false) {
87
- await releaseActionService.deleteManyForContentType(contentType.uid);
88
- }
89
- }
90
- );
91
- eventManager.addDestroyListenerCallback(destroyContentTypeUpdateListener);
92
- const destroyContentTypeDeleteListener = strapi2.eventHub.on(
93
- "content-type.delete",
94
- async ({ contentType }) => {
95
- await releaseActionService.deleteManyForContentType(contentType.uid);
96
- }
97
- );
98
- eventManager.addDestroyListenerCallback(destroyContentTypeDeleteListener);
105
+ strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
106
+ strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
99
107
  }
100
108
  };
101
109
  const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
102
110
  const bootstrap = async ({ strapi: strapi2 }) => {
103
- if (features$1.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
111
+ if (features$1.isEnabled("cms-content-releases")) {
104
112
  strapi2.db.lifecycles.subscribe({
105
113
  afterDelete(event) {
106
114
  const { model, result } = event;
107
- if (model.kind === "collectionType" && model.options.draftAndPublish) {
115
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
108
116
  const { id } = result;
109
117
  strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
110
118
  where: {
@@ -120,7 +128,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
120
128
  */
121
129
  async beforeDeleteMany(event) {
122
130
  const { model, params } = event;
123
- if (model.kind === "collectionType" && model.options.draftAndPublish) {
131
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
124
132
  const { where } = params;
125
133
  const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
126
134
  event.state.entriesToDelete = entriesToDelete;
@@ -235,15 +243,9 @@ const contentTypes = {
235
243
  release: release$1,
236
244
  "release-action": releaseAction$1
237
245
  };
238
- const createReleaseActionService = ({ strapi: strapi2 }) => ({
239
- async deleteManyForContentType(contentTypeUid) {
240
- return strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
241
- where: {
242
- target_type: contentTypeUid
243
- }
244
- });
245
- }
246
- });
246
+ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
247
+ return strapi2.plugin("content-releases").service(name);
248
+ };
247
249
  const getGroupName = (queryValue) => {
248
250
  switch (queryValue) {
249
251
  case "contentType":
@@ -259,7 +261,14 @@ const getGroupName = (queryValue) => {
259
261
  const createReleaseService = ({ strapi: strapi2 }) => ({
260
262
  async create(releaseData, { user }) {
261
263
  const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
262
- await getService("release-validation", { strapi: strapi2 }).validatePendingReleasesLimit();
264
+ const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease } = getService(
265
+ "release-validation",
266
+ { strapi: strapi2 }
267
+ );
268
+ await Promise.all([
269
+ validatePendingReleasesLimit(),
270
+ validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
271
+ ]);
263
272
  return strapi2.entityService.create(RELEASE_MODEL_UID, {
264
273
  data: releaseWithCreatorFields
265
274
  });
@@ -281,51 +290,66 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
281
290
  }
282
291
  });
283
292
  },
284
- async findManyForContentTypeEntry(contentTypeUid, entryId, {
285
- hasEntryAttached
286
- } = {
287
- hasEntryAttached: false
288
- }) {
289
- const whereActions = hasEntryAttached ? {
290
- // Find all Releases where the content type entry is present
291
- actions: {
292
- target_type: contentTypeUid,
293
- target_id: entryId
294
- }
295
- } : {
296
- // Find all Releases where the content type entry is not present
297
- $or: [
298
- {
299
- $not: {
300
- actions: {
301
- target_type: contentTypeUid,
302
- target_id: entryId
303
- }
304
- }
293
+ async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
294
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
295
+ where: {
296
+ actions: {
297
+ target_type: contentTypeUid,
298
+ target_id: entryId
305
299
  },
306
- {
307
- actions: null
300
+ releasedAt: {
301
+ $null: true
308
302
  }
309
- ]
310
- };
311
- const populateAttachedAction = hasEntryAttached ? {
312
- // Filter the action to get only the content type entry
313
- actions: {
314
- where: {
303
+ },
304
+ populate: {
305
+ // Filter the action to get only the content type entry
306
+ actions: {
307
+ where: {
308
+ target_type: contentTypeUid,
309
+ target_id: entryId
310
+ }
311
+ }
312
+ }
313
+ });
314
+ return releases.map((release2) => {
315
+ if (release2.actions?.length) {
316
+ const [actionForEntry] = release2.actions;
317
+ delete release2.actions;
318
+ return {
319
+ ...release2,
320
+ action: actionForEntry
321
+ };
322
+ }
323
+ return release2;
324
+ });
325
+ },
326
+ async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
327
+ const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
328
+ where: {
329
+ releasedAt: {
330
+ $null: true
331
+ },
332
+ actions: {
315
333
  target_type: contentTypeUid,
316
334
  target_id: entryId
317
335
  }
318
336
  }
319
- } : {};
337
+ });
320
338
  const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
321
339
  where: {
322
- ...whereActions,
340
+ $or: [
341
+ {
342
+ id: {
343
+ $notIn: releasesRelated.map((release2) => release2.id)
344
+ }
345
+ },
346
+ {
347
+ actions: null
348
+ }
349
+ ],
323
350
  releasedAt: {
324
351
  $null: true
325
352
  }
326
- },
327
- populate: {
328
- ...populateAttachedAction
329
353
  }
330
354
  });
331
355
  return releases.map((release2) => {
@@ -524,7 +548,9 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
524
548
  populate: {
525
549
  actions: {
526
550
  populate: {
527
- entry: true
551
+ entry: {
552
+ fields: ["id"]
553
+ }
528
554
  }
529
555
  }
530
556
  }
@@ -544,25 +570,49 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
544
570
  const contentTypeUid = action.contentType;
545
571
  if (!actions[contentTypeUid]) {
546
572
  actions[contentTypeUid] = {
547
- publish: [],
548
- unpublish: []
573
+ entriestoPublishIds: [],
574
+ entriesToUnpublishIds: []
549
575
  };
550
576
  }
551
577
  if (action.type === "publish") {
552
- actions[contentTypeUid].publish.push(action.entry);
578
+ actions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
553
579
  } else {
554
- actions[contentTypeUid].unpublish.push(action.entry);
580
+ actions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
555
581
  }
556
582
  }
557
583
  const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
584
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
558
585
  await strapi2.db.transaction(async () => {
559
586
  for (const contentTypeUid of Object.keys(actions)) {
560
- const { publish, unpublish } = actions[contentTypeUid];
561
- if (publish.length > 0) {
562
- await entityManagerService.publishMany(publish, contentTypeUid);
587
+ const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
588
+ const { entriestoPublishIds, entriesToUnpublishIds } = actions[contentTypeUid];
589
+ const entriesToPublish = await strapi2.entityService.findMany(
590
+ contentTypeUid,
591
+ {
592
+ filters: {
593
+ id: {
594
+ $in: entriestoPublishIds
595
+ }
596
+ },
597
+ populate
598
+ }
599
+ );
600
+ const entriesToUnpublish = await strapi2.entityService.findMany(
601
+ contentTypeUid,
602
+ {
603
+ filters: {
604
+ id: {
605
+ $in: entriesToUnpublishIds
606
+ }
607
+ },
608
+ populate
609
+ }
610
+ );
611
+ if (entriesToPublish.length > 0) {
612
+ await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
563
613
  }
564
- if (unpublish.length > 0) {
565
- await entityManagerService.unpublishMany(unpublish, contentTypeUid);
614
+ if (entriesToUnpublish.length > 0) {
615
+ await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
566
616
  }
567
617
  }
568
618
  });
@@ -660,31 +710,25 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
660
710
  if (pendingReleasesCount >= maximumPendingReleases) {
661
711
  throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
662
712
  }
663
- }
664
- });
665
- const createEventManagerService = () => {
666
- const state = {
667
- destroyListenerCallbacks: []
668
- };
669
- return {
670
- addDestroyListenerCallback(destroyListenerCallback) {
671
- state.destroyListenerCallbacks.push(destroyListenerCallback);
672
- },
673
- destroyAllListeners() {
674
- if (!state.destroyListenerCallbacks.length) {
675
- return;
713
+ },
714
+ async validateUniqueNameForPendingRelease(name) {
715
+ const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
716
+ filters: {
717
+ releasedAt: {
718
+ $null: true
719
+ },
720
+ name
676
721
  }
677
- state.destroyListenerCallbacks.forEach((destroyListenerCallback) => {
678
- destroyListenerCallback();
679
- });
722
+ });
723
+ const isNameUnique = pendingReleases.length === 0;
724
+ if (!isNameUnique) {
725
+ throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
680
726
  }
681
- };
682
- };
727
+ }
728
+ });
683
729
  const services = {
684
730
  release: createReleaseService,
685
- "release-action": createReleaseActionService,
686
- "release-validation": createReleaseValidationService,
687
- "event-manager": createEventManagerService
731
+ "release-validation": createReleaseValidationService
688
732
  };
689
733
  const RELEASE_SCHEMA = yup__namespace.object().shape({
690
734
  name: yup__namespace.string().trim().required()
@@ -704,9 +748,7 @@ const releaseController = {
704
748
  const contentTypeUid = query.contentTypeUid;
705
749
  const entryId = query.entryId;
706
750
  const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
707
- const data = await releaseService.findManyForContentTypeEntry(contentTypeUid, entryId, {
708
- hasEntryAttached
709
- });
751
+ const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
710
752
  ctx.body = { data };
711
753
  } else {
712
754
  const query = await permissionsManager.sanitizeQuery(ctx.query);
@@ -1063,19 +1105,14 @@ const routes = {
1063
1105
  };
1064
1106
  const { features } = require("@strapi/strapi/dist/utils/ee");
1065
1107
  const getPlugin = () => {
1066
- if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
1108
+ if (features.isEnabled("cms-content-releases")) {
1067
1109
  return {
1068
1110
  register,
1069
1111
  bootstrap,
1070
1112
  contentTypes,
1071
1113
  services,
1072
1114
  controllers,
1073
- routes,
1074
- destroy() {
1075
- if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
1076
- getService("event-manager").destroyAllListeners();
1077
- }
1078
- }
1115
+ routes
1079
1116
  };
1080
1117
  }
1081
1118
  return {