@strapi/content-releases 0.0.0-experimental.f7b9b47085e387e97f990d8695971b51d7f7149a → 0.0.0-next.37dd1e3ff22e1635b69683abadd444912ae0dbff

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,4 @@
1
- import { h } from "../_chunks/index-937f8179.mjs";
1
+ import { j } from "../_chunks/index-_Zsj8MUA.mjs";
2
2
  import "@strapi/helper-plugin";
3
3
  import "@strapi/icons";
4
4
  import "react/jsx-runtime";
@@ -14,6 +14,6 @@ import "yup";
14
14
  import "@reduxjs/toolkit/query/react";
15
15
  import "styled-components";
16
16
  export {
17
- h as default
17
+ j as default
18
18
  };
19
19
  //# sourceMappingURL=index.mjs.map
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  const utils = require("@strapi/utils");
3
+ const _ = require("lodash/fp");
3
4
  const yup = require("yup");
5
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
4
6
  function _interopNamespace(e) {
5
7
  if (e && e.__esModule)
6
8
  return e;
@@ -19,6 +21,7 @@ function _interopNamespace(e) {
19
21
  n.default = e;
20
22
  return Object.freeze(n);
21
23
  }
24
+ const ___default = /* @__PURE__ */ _interopDefault(_);
22
25
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
23
26
  const RELEASE_MODEL_UID = "plugin::content-releases.release";
24
27
  const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
@@ -66,10 +69,80 @@ const ACTIONS = [
66
69
  pluginName: "content-releases"
67
70
  }
68
71
  ];
69
- const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
72
+ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
73
+ return strapi2.plugin("content-releases").service(name);
74
+ };
75
+ const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
70
76
  const register = async ({ strapi: strapi2 }) => {
71
- if (features$1.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
77
+ if (features$2.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
72
78
  await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
79
+ const releaseActionService = getService("release-action", { strapi: strapi2 });
80
+ const eventManager = getService("event-manager", { strapi: strapi2 });
81
+ const destroyContentTypeUpdateListener = strapi2.eventHub.on(
82
+ "content-type.update",
83
+ async ({ contentType }) => {
84
+ if (contentType.schema.options.draftAndPublish === false) {
85
+ await releaseActionService.deleteManyForContentType(contentType.uid);
86
+ }
87
+ }
88
+ );
89
+ eventManager.addDestroyListenerCallback(destroyContentTypeUpdateListener);
90
+ const destroyContentTypeDeleteListener = strapi2.eventHub.on(
91
+ "content-type.delete",
92
+ async ({ contentType }) => {
93
+ await releaseActionService.deleteManyForContentType(contentType.uid);
94
+ }
95
+ );
96
+ eventManager.addDestroyListenerCallback(destroyContentTypeDeleteListener);
97
+ }
98
+ };
99
+ const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
100
+ const bootstrap = async ({ strapi: strapi2 }) => {
101
+ if (features$1.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
102
+ strapi2.db.lifecycles.subscribe({
103
+ afterDelete(event) {
104
+ const { model, result } = event;
105
+ if (model.kind === "collectionType" && model.options.draftAndPublish) {
106
+ const { id } = result;
107
+ strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
108
+ where: {
109
+ target_type: model.uid,
110
+ target_id: id
111
+ }
112
+ });
113
+ }
114
+ },
115
+ /**
116
+ * deleteMany hook doesn't return the deleted entries ids
117
+ * so we need to fetch them before deleting the entries to save the ids on our state
118
+ */
119
+ async beforeDeleteMany(event) {
120
+ const { model, params } = event;
121
+ if (model.kind === "collectionType" && model.options.draftAndPublish) {
122
+ const { where } = params;
123
+ const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
124
+ event.state.entriesToDelete = entriesToDelete;
125
+ }
126
+ },
127
+ /**
128
+ * We delete the release actions related to deleted entries
129
+ * We make this only after deleteMany is succesfully executed to avoid errors
130
+ */
131
+ async afterDeleteMany(event) {
132
+ const { model, state } = event;
133
+ const entriesToDelete = state.entriesToDelete;
134
+ if (entriesToDelete) {
135
+ await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
136
+ where: {
137
+ target_type: model.uid,
138
+ target_id: {
139
+ $in: entriesToDelete.map((entry) => entry.id)
140
+ }
141
+ }
142
+ });
143
+ }
144
+ }
145
+ });
73
146
  }
74
147
  };
75
148
  const schema$1 = {
@@ -142,6 +215,9 @@ const schema = {
142
215
  type: "string",
143
216
  required: true
144
217
  },
218
+ locale: {
219
+ type: "string"
220
+ },
145
221
  release: {
146
222
  type: "relation",
147
223
  relation: "manyToOne",
@@ -157,8 +233,26 @@ const contentTypes = {
157
233
  release: release$1,
158
234
  "release-action": releaseAction$1
159
235
  };
160
- const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
161
- return strapi2.plugin("content-releases").service(name);
236
+ const createReleaseActionService = ({ strapi: strapi2 }) => ({
237
+ async deleteManyForContentType(contentTypeUid) {
238
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
239
+ where: {
240
+ target_type: contentTypeUid
241
+ }
242
+ });
243
+ }
244
+ });
245
+ const getGroupName = (queryValue) => {
246
+ switch (queryValue) {
247
+ case "contentType":
248
+ return "entry.contentType.displayName";
249
+ case "action":
250
+ return "type";
251
+ case "locale":
252
+ return ___default.default.getOr("No locale", "entry.locale.name");
253
+ default:
254
+ return "entry.contentType.displayName";
255
+ }
162
256
  };
163
257
  const createReleaseService = ({ strapi: strapi2 }) => ({
164
258
  async create(releaseData, { user }) {
@@ -244,19 +338,23 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
244
338
  });
245
339
  },
246
340
  async update(id, releaseData, { user }) {
247
- const updatedRelease = await utils.setCreatorFields({ user, isEdition: true })(releaseData);
248
- const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
341
+ const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(releaseData);
342
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
343
+ if (!release2) {
344
+ throw new utils.errors.NotFoundError(`No release found for id ${id}`);
345
+ }
346
+ if (release2.releasedAt) {
347
+ throw new utils.errors.ValidationError("Release already published");
348
+ }
349
+ const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
249
350
  /*
250
351
  * The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
251
352
  * is not compatible with the type we are passing here: UpdateRelease.Request['body']
252
353
  */
253
354
  // @ts-expect-error see above
254
- data: updatedRelease
355
+ data: releaseWithCreatorFields
255
356
  });
256
- if (!release2) {
257
- throw new utils.errors.NotFoundError(`No release found for id ${id}`);
258
- }
259
- return release2;
357
+ return updatedRelease;
260
358
  },
261
359
  async createAction(releaseId, action) {
262
360
  const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
@@ -266,11 +364,19 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
266
364
  validateEntryContentType(action.entry.contentType),
267
365
  validateUniqueEntry(releaseId, action)
268
366
  ]);
367
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
368
+ if (!release2) {
369
+ throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
370
+ }
371
+ if (release2.releasedAt) {
372
+ throw new utils.errors.ValidationError("Release already published");
373
+ }
269
374
  const { entry, type } = action;
270
375
  return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
271
376
  data: {
272
377
  type,
273
378
  contentType: entry.contentType,
379
+ locale: entry.locale,
274
380
  entry: {
275
381
  id: entry.id,
276
382
  __type: entry.contentType,
@@ -282,8 +388,10 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
282
388
  });
283
389
  },
284
390
  async findActions(releaseId, query) {
285
- const result = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
286
- if (!result) {
391
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
392
+ fields: ["id"]
393
+ });
394
+ if (!release2) {
287
395
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
288
396
  }
289
397
  return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
@@ -299,18 +407,40 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
299
407
  async countActions(query) {
300
408
  return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
301
409
  },
302
- async getAllContentTypeUids(releaseId) {
303
- const contentTypesFromReleaseActions = await strapi2.db.queryBuilder(RELEASE_ACTION_MODEL_UID).select("content_type").where({
304
- $and: [
305
- {
306
- release: releaseId
410
+ async groupActions(actions, groupBy) {
411
+ const contentTypeUids = actions.reduce((acc, action) => {
412
+ if (!acc.includes(action.contentType)) {
413
+ acc.push(action.contentType);
414
+ }
415
+ return acc;
416
+ }, []);
417
+ const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
418
+ contentTypeUids
419
+ );
420
+ const allLocales = await strapi2.plugin("i18n").service("locales").find();
421
+ const allLocalesDictionary = allLocales.reduce((acc, locale) => {
422
+ acc[locale.code] = { name: locale.name, code: locale.code };
423
+ return acc;
424
+ }, {});
425
+ const formattedData = actions.map((action) => {
426
+ const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
427
+ return {
428
+ ...action,
429
+ entry: {
430
+ id: action.entry.id,
431
+ contentType: {
432
+ displayName,
433
+ mainFieldValue: action.entry[mainField]
434
+ },
435
+ locale: action.locale ? allLocalesDictionary[action.locale] : null,
436
+ status: action.entry.publishedAt ? "published" : "draft"
307
437
  }
308
- ]
309
- }).groupBy("content_type").execute();
310
- return contentTypesFromReleaseActions.map(({ contentType: contentTypeUid }) => contentTypeUid);
438
+ };
439
+ });
440
+ const groupName = getGroupName(groupBy);
441
+ return ___default.default.groupBy(groupName)(formattedData);
311
442
  },
312
- async getContentTypesDataForActions(releaseId) {
313
- const contentTypesUids = await this.getAllContentTypeUids(releaseId);
443
+ async getContentTypesDataForActions(contentTypesUids) {
314
444
  const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
315
445
  const contentTypesData = {};
316
446
  for (const contentTypeUid of contentTypesUids) {
@@ -415,13 +545,18 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
415
545
  const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
416
546
  where: {
417
547
  id: actionId,
418
- release: releaseId
548
+ release: {
549
+ id: releaseId,
550
+ releasedAt: {
551
+ $null: true
552
+ }
553
+ }
419
554
  },
420
555
  data: update
421
556
  });
422
557
  if (!updatedAction) {
423
558
  throw new utils.errors.NotFoundError(
424
- `Action with id ${actionId} not found in release with id ${releaseId}`
559
+ `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
425
560
  );
426
561
  }
427
562
  return updatedAction;
@@ -430,12 +565,17 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
430
565
  const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
431
566
  where: {
432
567
  id: actionId,
433
- release: releaseId
568
+ release: {
569
+ id: releaseId,
570
+ releasedAt: {
571
+ $null: true
572
+ }
573
+ }
434
574
  }
435
575
  });
436
576
  if (!deletedAction) {
437
577
  throw new utils.errors.NotFoundError(
438
- `Action with id ${actionId} not found in release with id ${releaseId}`
578
+ `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
439
579
  );
440
580
  }
441
581
  return deletedAction;
@@ -470,7 +610,30 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
470
610
  }
471
611
  }
472
612
  });
473
- const services = { release: createReleaseService, "release-validation": createReleaseValidationService };
613
+ const createEventManagerService = () => {
614
+ const state = {
615
+ destroyListenerCallbacks: []
616
+ };
617
+ return {
618
+ addDestroyListenerCallback(destroyListenerCallback) {
619
+ state.destroyListenerCallbacks.push(destroyListenerCallback);
620
+ },
621
+ destroyAllListeners() {
622
+ if (!state.destroyListenerCallbacks.length) {
623
+ return;
624
+ }
625
+ state.destroyListenerCallbacks.forEach((destroyListenerCallback) => {
626
+ destroyListenerCallback();
627
+ });
628
+ }
629
+ };
630
+ };
631
+ const services = {
632
+ release: createReleaseService,
633
+ "release-action": createReleaseActionService,
634
+ "release-validation": createReleaseValidationService,
635
+ "event-manager": createEventManagerService
636
+ };
474
637
  const RELEASE_SCHEMA = yup__namespace.object().shape({
475
638
  name: yup__namespace.string().trim().required()
476
639
  }).required().noUnknown();
@@ -615,31 +778,13 @@ const releaseActionController = {
615
778
  });
616
779
  const query = await permissionsManager.sanitizeQuery(ctx.query);
617
780
  const releaseService = getService("release", { strapi });
618
- const { results, pagination } = await releaseService.findActions(releaseId, query);
619
- const allReleaseContentTypesDictionary = await releaseService.getContentTypesDataForActions(
620
- releaseId
621
- );
622
- const allLocales = await strapi.plugin("i18n").service("locales").find();
623
- const allLocalesDictionary = allLocales.reduce((acc, locale) => {
624
- acc[locale.code] = { name: locale.name, code: locale.code };
625
- return acc;
626
- }, {});
627
- const data = results.map((action) => {
628
- const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
629
- return {
630
- ...action,
631
- entry: {
632
- id: action.entry.id,
633
- contentType: {
634
- displayName,
635
- mainFieldValue: action.entry[mainField]
636
- },
637
- locale: allLocalesDictionary[action.entry.locale]
638
- }
639
- };
781
+ const { results, pagination } = await releaseService.findActions(releaseId, {
782
+ sort: query.groupBy === "action" ? "type" : query.groupBy,
783
+ ...query
640
784
  });
785
+ const groupedData = await releaseService.groupActions(results, query.groupBy);
641
786
  ctx.body = {
642
- data,
787
+ data: groupedData,
643
788
  meta: {
644
789
  pagination
645
790
  }
@@ -663,10 +808,8 @@ const releaseActionController = {
663
808
  async delete(ctx) {
664
809
  const actionId = ctx.params.actionId;
665
810
  const releaseId = ctx.params.releaseId;
666
- const deletedReleaseAction = await getService("release", { strapi }).deleteAction(
667
- actionId,
668
- releaseId
669
- );
811
+ const releaseService = getService("release", { strapi });
812
+ const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
670
813
  ctx.body = {
671
814
  data: deletedReleaseAction
672
815
  };
@@ -852,10 +995,16 @@ const getPlugin = () => {
852
995
  if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
853
996
  return {
854
997
  register,
998
+ bootstrap,
855
999
  contentTypes,
856
1000
  services,
857
1001
  controllers,
858
- routes
1002
+ routes,
1003
+ destroy() {
1004
+ if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
1005
+ getService("event-manager").destroyAllListeners();
1006
+ }
1007
+ }
859
1008
  };
860
1009
  }
861
1010
  return {