@strapi/content-releases 0.0.0-experimental.f7b9b47085e387e97f990d8695971b51d7f7149a → 0.0.0-next.2b10ca9b97a5854909ba0a8d1d5b00f73cae58fa
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.
- package/dist/_chunks/{App-f2cafd81.js → App-5PRKHpa2.js} +207 -112
- package/dist/_chunks/App-5PRKHpa2.js.map +1 -0
- package/dist/_chunks/{App-a4843fda.mjs → App-J4jrthEu.mjs} +210 -115
- package/dist/_chunks/App-J4jrthEu.mjs.map +1 -0
- package/dist/_chunks/{en-13576ce2.js → en-haKSQIo8.js} +13 -4
- package/dist/_chunks/en-haKSQIo8.js.map +1 -0
- package/dist/_chunks/{en-e98d8b57.mjs → en-ngTk74JV.mjs} +13 -4
- package/dist/_chunks/en-ngTk74JV.mjs.map +1 -0
- package/dist/_chunks/{index-66d129ac.js → index-PEkKIRyJ.js} +33 -22
- package/dist/_chunks/index-PEkKIRyJ.js.map +1 -0
- package/dist/_chunks/{index-937f8179.mjs → index-_Zsj8MUA.mjs} +36 -25
- package/dist/_chunks/index-_Zsj8MUA.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +2 -2
- package/dist/server/index.js +224 -56
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +221 -56
- package/dist/server/index.mjs.map +1 -1
- package/package.json +9 -8
- package/dist/_chunks/App-a4843fda.mjs.map +0 -1
- package/dist/_chunks/App-f2cafd81.js.map +0 -1
- package/dist/_chunks/en-13576ce2.js.map +0 -1
- package/dist/_chunks/en-e98d8b57.mjs.map +0 -1
- package/dist/_chunks/index-66d129ac.js.map +0 -1
- package/dist/_chunks/index-937f8179.mjs.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { setCreatorFields, errors, validateYupSchema, yup as yup$1 } from "@strapi/utils";
|
|
2
|
+
import _ from "lodash/fp";
|
|
3
|
+
import EE from "@strapi/strapi/dist/utils/ee";
|
|
2
4
|
import * as yup from "yup";
|
|
3
5
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
4
6
|
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
@@ -46,10 +48,80 @@ const ACTIONS = [
|
|
|
46
48
|
pluginName: "content-releases"
|
|
47
49
|
}
|
|
48
50
|
];
|
|
49
|
-
const {
|
|
51
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
52
|
+
return strapi2.plugin("content-releases").service(name);
|
|
53
|
+
};
|
|
54
|
+
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
50
55
|
const register = async ({ strapi: strapi2 }) => {
|
|
51
|
-
if (features$
|
|
56
|
+
if (features$2.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
|
|
52
57
|
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);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
79
|
+
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
80
|
+
if (features$1.isEnabled("cms-content-releases") && strapi2.features.future.isEnabled("contentReleases")) {
|
|
81
|
+
strapi2.db.lifecycles.subscribe({
|
|
82
|
+
afterDelete(event) {
|
|
83
|
+
const { model, result } = event;
|
|
84
|
+
if (model.kind === "collectionType" && model.options.draftAndPublish) {
|
|
85
|
+
const { id } = result;
|
|
86
|
+
strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
87
|
+
where: {
|
|
88
|
+
target_type: model.uid,
|
|
89
|
+
target_id: id
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
/**
|
|
95
|
+
* deleteMany hook doesn't return the deleted entries ids
|
|
96
|
+
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
97
|
+
*/
|
|
98
|
+
async beforeDeleteMany(event) {
|
|
99
|
+
const { model, params } = event;
|
|
100
|
+
if (model.kind === "collectionType" && model.options.draftAndPublish) {
|
|
101
|
+
const { where } = params;
|
|
102
|
+
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
103
|
+
event.state.entriesToDelete = entriesToDelete;
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* We delete the release actions related to deleted entries
|
|
108
|
+
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
109
|
+
*/
|
|
110
|
+
async afterDeleteMany(event) {
|
|
111
|
+
const { model, state } = event;
|
|
112
|
+
const entriesToDelete = state.entriesToDelete;
|
|
113
|
+
if (entriesToDelete) {
|
|
114
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
115
|
+
where: {
|
|
116
|
+
target_type: model.uid,
|
|
117
|
+
target_id: {
|
|
118
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
53
125
|
}
|
|
54
126
|
};
|
|
55
127
|
const schema$1 = {
|
|
@@ -122,6 +194,9 @@ const schema = {
|
|
|
122
194
|
type: "string",
|
|
123
195
|
required: true
|
|
124
196
|
},
|
|
197
|
+
locale: {
|
|
198
|
+
type: "string"
|
|
199
|
+
},
|
|
125
200
|
release: {
|
|
126
201
|
type: "relation",
|
|
127
202
|
relation: "manyToOne",
|
|
@@ -137,12 +212,31 @@ const contentTypes = {
|
|
|
137
212
|
release: release$1,
|
|
138
213
|
"release-action": releaseAction$1
|
|
139
214
|
};
|
|
140
|
-
const
|
|
141
|
-
|
|
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
|
+
});
|
|
224
|
+
const getGroupName = (queryValue) => {
|
|
225
|
+
switch (queryValue) {
|
|
226
|
+
case "contentType":
|
|
227
|
+
return "entry.contentType.displayName";
|
|
228
|
+
case "action":
|
|
229
|
+
return "type";
|
|
230
|
+
case "locale":
|
|
231
|
+
return _.getOr("No locale", "entry.locale.name");
|
|
232
|
+
default:
|
|
233
|
+
return "entry.contentType.displayName";
|
|
234
|
+
}
|
|
142
235
|
};
|
|
143
236
|
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
144
237
|
async create(releaseData, { user }) {
|
|
145
238
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
239
|
+
await getService("release-validation", { strapi: strapi2 }).validatePendingReleasesLimit();
|
|
146
240
|
return strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
147
241
|
data: releaseWithCreatorFields
|
|
148
242
|
});
|
|
@@ -224,19 +318,23 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
224
318
|
});
|
|
225
319
|
},
|
|
226
320
|
async update(id, releaseData, { user }) {
|
|
227
|
-
const
|
|
228
|
-
const release2 = await strapi2.entityService.
|
|
321
|
+
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(releaseData);
|
|
322
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
323
|
+
if (!release2) {
|
|
324
|
+
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
325
|
+
}
|
|
326
|
+
if (release2.releasedAt) {
|
|
327
|
+
throw new errors.ValidationError("Release already published");
|
|
328
|
+
}
|
|
329
|
+
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
229
330
|
/*
|
|
230
331
|
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
231
332
|
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
232
333
|
*/
|
|
233
334
|
// @ts-expect-error see above
|
|
234
|
-
data:
|
|
335
|
+
data: releaseWithCreatorFields
|
|
235
336
|
});
|
|
236
|
-
|
|
237
|
-
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
238
|
-
}
|
|
239
|
-
return release2;
|
|
337
|
+
return updatedRelease;
|
|
240
338
|
},
|
|
241
339
|
async createAction(releaseId, action) {
|
|
242
340
|
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
@@ -246,11 +344,19 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
246
344
|
validateEntryContentType(action.entry.contentType),
|
|
247
345
|
validateUniqueEntry(releaseId, action)
|
|
248
346
|
]);
|
|
347
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
348
|
+
if (!release2) {
|
|
349
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
350
|
+
}
|
|
351
|
+
if (release2.releasedAt) {
|
|
352
|
+
throw new errors.ValidationError("Release already published");
|
|
353
|
+
}
|
|
249
354
|
const { entry, type } = action;
|
|
250
355
|
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
251
356
|
data: {
|
|
252
357
|
type,
|
|
253
358
|
contentType: entry.contentType,
|
|
359
|
+
locale: entry.locale,
|
|
254
360
|
entry: {
|
|
255
361
|
id: entry.id,
|
|
256
362
|
__type: entry.contentType,
|
|
@@ -262,8 +368,10 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
262
368
|
});
|
|
263
369
|
},
|
|
264
370
|
async findActions(releaseId, query) {
|
|
265
|
-
const
|
|
266
|
-
|
|
371
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
372
|
+
fields: ["id"]
|
|
373
|
+
});
|
|
374
|
+
if (!release2) {
|
|
267
375
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
268
376
|
}
|
|
269
377
|
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
@@ -279,18 +387,40 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
279
387
|
async countActions(query) {
|
|
280
388
|
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
281
389
|
},
|
|
282
|
-
async
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
390
|
+
async groupActions(actions, groupBy) {
|
|
391
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
392
|
+
if (!acc.includes(action.contentType)) {
|
|
393
|
+
acc.push(action.contentType);
|
|
394
|
+
}
|
|
395
|
+
return acc;
|
|
396
|
+
}, []);
|
|
397
|
+
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
398
|
+
contentTypeUids
|
|
399
|
+
);
|
|
400
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find();
|
|
401
|
+
const allLocalesDictionary = allLocales.reduce((acc, locale) => {
|
|
402
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
403
|
+
return acc;
|
|
404
|
+
}, {});
|
|
405
|
+
const formattedData = actions.map((action) => {
|
|
406
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
407
|
+
return {
|
|
408
|
+
...action,
|
|
409
|
+
entry: {
|
|
410
|
+
id: action.entry.id,
|
|
411
|
+
contentType: {
|
|
412
|
+
displayName,
|
|
413
|
+
mainFieldValue: action.entry[mainField]
|
|
414
|
+
},
|
|
415
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
416
|
+
status: action.entry.publishedAt ? "published" : "draft"
|
|
287
417
|
}
|
|
288
|
-
|
|
289
|
-
})
|
|
290
|
-
|
|
418
|
+
};
|
|
419
|
+
});
|
|
420
|
+
const groupName = getGroupName(groupBy);
|
|
421
|
+
return _.groupBy(groupName)(formattedData);
|
|
291
422
|
},
|
|
292
|
-
async getContentTypesDataForActions(
|
|
293
|
-
const contentTypesUids = await this.getAllContentTypeUids(releaseId);
|
|
423
|
+
async getContentTypesDataForActions(contentTypesUids) {
|
|
294
424
|
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
295
425
|
const contentTypesData = {};
|
|
296
426
|
for (const contentTypeUid of contentTypesUids) {
|
|
@@ -395,13 +525,18 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
395
525
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
396
526
|
where: {
|
|
397
527
|
id: actionId,
|
|
398
|
-
release:
|
|
528
|
+
release: {
|
|
529
|
+
id: releaseId,
|
|
530
|
+
releasedAt: {
|
|
531
|
+
$null: true
|
|
532
|
+
}
|
|
533
|
+
}
|
|
399
534
|
},
|
|
400
535
|
data: update
|
|
401
536
|
});
|
|
402
537
|
if (!updatedAction) {
|
|
403
538
|
throw new errors.NotFoundError(
|
|
404
|
-
`Action with id ${actionId} not found in release with id ${releaseId}`
|
|
539
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
405
540
|
);
|
|
406
541
|
}
|
|
407
542
|
return updatedAction;
|
|
@@ -410,12 +545,17 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
410
545
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
411
546
|
where: {
|
|
412
547
|
id: actionId,
|
|
413
|
-
release:
|
|
548
|
+
release: {
|
|
549
|
+
id: releaseId,
|
|
550
|
+
releasedAt: {
|
|
551
|
+
$null: true
|
|
552
|
+
}
|
|
553
|
+
}
|
|
414
554
|
}
|
|
415
555
|
});
|
|
416
556
|
if (!deletedAction) {
|
|
417
557
|
throw new errors.NotFoundError(
|
|
418
|
-
`Action with id ${actionId} not found in release with id ${releaseId}`
|
|
558
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
419
559
|
);
|
|
420
560
|
}
|
|
421
561
|
return deletedAction;
|
|
@@ -448,9 +588,48 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
448
588
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
449
589
|
);
|
|
450
590
|
}
|
|
591
|
+
},
|
|
592
|
+
async validatePendingReleasesLimit() {
|
|
593
|
+
const maximumPendingReleases = (
|
|
594
|
+
// @ts-expect-error - options is not typed into features
|
|
595
|
+
EE.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
596
|
+
);
|
|
597
|
+
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
598
|
+
filters: {
|
|
599
|
+
releasedAt: {
|
|
600
|
+
$null: true
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
if (pendingReleasesCount >= maximumPendingReleases) {
|
|
605
|
+
throw new errors.ValidationError("You have reached the maximum number of pending releases");
|
|
606
|
+
}
|
|
451
607
|
}
|
|
452
608
|
});
|
|
453
|
-
const
|
|
609
|
+
const createEventManagerService = () => {
|
|
610
|
+
const state = {
|
|
611
|
+
destroyListenerCallbacks: []
|
|
612
|
+
};
|
|
613
|
+
return {
|
|
614
|
+
addDestroyListenerCallback(destroyListenerCallback) {
|
|
615
|
+
state.destroyListenerCallbacks.push(destroyListenerCallback);
|
|
616
|
+
},
|
|
617
|
+
destroyAllListeners() {
|
|
618
|
+
if (!state.destroyListenerCallbacks.length) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
state.destroyListenerCallbacks.forEach((destroyListenerCallback) => {
|
|
622
|
+
destroyListenerCallback();
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
};
|
|
627
|
+
const services = {
|
|
628
|
+
release: createReleaseService,
|
|
629
|
+
"release-action": createReleaseActionService,
|
|
630
|
+
"release-validation": createReleaseValidationService,
|
|
631
|
+
"event-manager": createEventManagerService
|
|
632
|
+
};
|
|
454
633
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
455
634
|
name: yup.string().trim().required()
|
|
456
635
|
}).required().noUnknown();
|
|
@@ -595,31 +774,13 @@ const releaseActionController = {
|
|
|
595
774
|
});
|
|
596
775
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
597
776
|
const releaseService = getService("release", { strapi });
|
|
598
|
-
const { results, pagination } = await releaseService.findActions(releaseId,
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
);
|
|
602
|
-
const allLocales = await strapi.plugin("i18n").service("locales").find();
|
|
603
|
-
const allLocalesDictionary = allLocales.reduce((acc, locale) => {
|
|
604
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
605
|
-
return acc;
|
|
606
|
-
}, {});
|
|
607
|
-
const data = results.map((action) => {
|
|
608
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
609
|
-
return {
|
|
610
|
-
...action,
|
|
611
|
-
entry: {
|
|
612
|
-
id: action.entry.id,
|
|
613
|
-
contentType: {
|
|
614
|
-
displayName,
|
|
615
|
-
mainFieldValue: action.entry[mainField]
|
|
616
|
-
},
|
|
617
|
-
locale: allLocalesDictionary[action.entry.locale]
|
|
618
|
-
}
|
|
619
|
-
};
|
|
777
|
+
const { results, pagination } = await releaseService.findActions(releaseId, {
|
|
778
|
+
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
779
|
+
...query
|
|
620
780
|
});
|
|
781
|
+
const groupedData = await releaseService.groupActions(results, query.groupBy);
|
|
621
782
|
ctx.body = {
|
|
622
|
-
data,
|
|
783
|
+
data: groupedData,
|
|
623
784
|
meta: {
|
|
624
785
|
pagination
|
|
625
786
|
}
|
|
@@ -643,10 +804,8 @@ const releaseActionController = {
|
|
|
643
804
|
async delete(ctx) {
|
|
644
805
|
const actionId = ctx.params.actionId;
|
|
645
806
|
const releaseId = ctx.params.releaseId;
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
releaseId
|
|
649
|
-
);
|
|
807
|
+
const releaseService = getService("release", { strapi });
|
|
808
|
+
const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
|
|
650
809
|
ctx.body = {
|
|
651
810
|
data: deletedReleaseAction
|
|
652
811
|
};
|
|
@@ -832,10 +991,16 @@ const getPlugin = () => {
|
|
|
832
991
|
if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
|
|
833
992
|
return {
|
|
834
993
|
register,
|
|
994
|
+
bootstrap,
|
|
835
995
|
contentTypes,
|
|
836
996
|
services,
|
|
837
997
|
controllers,
|
|
838
|
-
routes
|
|
998
|
+
routes,
|
|
999
|
+
destroy() {
|
|
1000
|
+
if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
|
|
1001
|
+
getService("event-manager").destroyAllListeners();
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
839
1004
|
};
|
|
840
1005
|
}
|
|
841
1006
|
return {
|