@strapi/content-releases 0.0.0-next.90a86f595c31de9a89f4255318bfb0cccb30ceed → 0.0.0-next.a9d79bec775daaf0da4e506b2aebafdb4ca95b06
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-8FCxPK8-.mjs → App-bpzO2Ljh.mjs} +616 -365
- package/dist/_chunks/App-bpzO2Ljh.mjs.map +1 -0
- package/dist/_chunks/{App-pspKUC-W.js → App-p8aKBitd.js} +606 -354
- package/dist/_chunks/App-p8aKBitd.js.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
- package/dist/_chunks/{en-m9eTk4UF.mjs → en-WuuhP6Bn.mjs} +15 -4
- package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
- package/dist/_chunks/{en-r9YocBH0.js → en-gcJJ5htG.js} +15 -4
- package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
- package/dist/_chunks/{index-8aK7GzI5.mjs → index-AECgcaDa.mjs} +99 -22
- package/dist/_chunks/index-AECgcaDa.mjs.map +1 -0
- package/dist/_chunks/{index-nGaPcY9m.js → index-fP3qoWZ4.js} +88 -11
- package/dist/_chunks/index-fP3qoWZ4.js.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +2 -2
- package/dist/server/index.js +818 -432
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +817 -432
- package/dist/server/index.mjs.map +1 -1
- package/package.json +13 -11
- package/dist/_chunks/App-8FCxPK8-.mjs.map +0 -1
- package/dist/_chunks/App-pspKUC-W.js.map +0 -1
- package/dist/_chunks/en-m9eTk4UF.mjs.map +0 -1
- package/dist/_chunks/en-r9YocBH0.js.map +0 -1
- package/dist/_chunks/index-8aK7GzI5.mjs.map +0 -1
- package/dist/_chunks/index-nGaPcY9m.js.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { contentTypes as contentTypes$1, mapAsync, setCreatorFields, errors, validateYupSchema, yup as yup$1 } from "@strapi/utils";
|
|
2
|
+
import isEqual from "lodash/isEqual";
|
|
2
3
|
import { difference, keys } from "lodash";
|
|
3
4
|
import _ from "lodash/fp";
|
|
4
5
|
import EE from "@strapi/strapi/dist/utils/ee";
|
|
@@ -50,6 +51,32 @@ const ACTIONS = [
|
|
|
50
51
|
pluginName: "content-releases"
|
|
51
52
|
}
|
|
52
53
|
];
|
|
54
|
+
const ALLOWED_WEBHOOK_EVENTS = {
|
|
55
|
+
RELEASES_PUBLISH: "releases.publish"
|
|
56
|
+
};
|
|
57
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
58
|
+
return strapi2.plugin("content-releases").service(name);
|
|
59
|
+
};
|
|
60
|
+
const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
61
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
62
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
63
|
+
const entry = await strapi2.entityService.findOne(contentTypeUid, entryId, { populate });
|
|
64
|
+
return entry;
|
|
65
|
+
};
|
|
66
|
+
const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
67
|
+
try {
|
|
68
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
69
|
+
strapi2.getModel(contentTypeUid),
|
|
70
|
+
entry,
|
|
71
|
+
void 0,
|
|
72
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
73
|
+
entry
|
|
74
|
+
);
|
|
75
|
+
return true;
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
53
80
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
54
81
|
oldContentTypes,
|
|
55
82
|
contentTypes: contentTypes2
|
|
@@ -76,31 +103,196 @@ async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes:
|
|
|
76
103
|
});
|
|
77
104
|
}
|
|
78
105
|
}
|
|
106
|
+
async function migrateIsValidAndStatusReleases() {
|
|
107
|
+
const releasesWithoutStatus = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
108
|
+
where: {
|
|
109
|
+
status: null,
|
|
110
|
+
releasedAt: null
|
|
111
|
+
},
|
|
112
|
+
populate: {
|
|
113
|
+
actions: {
|
|
114
|
+
populate: {
|
|
115
|
+
entry: true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
mapAsync(releasesWithoutStatus, async (release2) => {
|
|
121
|
+
const actions = release2.actions;
|
|
122
|
+
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
123
|
+
for (const action of notValidatedActions) {
|
|
124
|
+
if (action.entry) {
|
|
125
|
+
const populatedEntry = await getPopulatedEntry(action.contentType, action.entry.id, {
|
|
126
|
+
strapi
|
|
127
|
+
});
|
|
128
|
+
if (populatedEntry) {
|
|
129
|
+
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
130
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
131
|
+
where: {
|
|
132
|
+
id: action.id
|
|
133
|
+
},
|
|
134
|
+
data: {
|
|
135
|
+
isEntryValid
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
142
|
+
});
|
|
143
|
+
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
144
|
+
where: {
|
|
145
|
+
status: null,
|
|
146
|
+
releasedAt: {
|
|
147
|
+
$notNull: true
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
mapAsync(publishedReleases, async (release2) => {
|
|
152
|
+
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
153
|
+
where: {
|
|
154
|
+
id: release2.id
|
|
155
|
+
},
|
|
156
|
+
data: {
|
|
157
|
+
status: "done"
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
163
|
+
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
164
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
|
|
165
|
+
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
166
|
+
);
|
|
167
|
+
const releasesAffected = /* @__PURE__ */ new Set();
|
|
168
|
+
mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
169
|
+
const oldContentType = oldContentTypes[contentTypeUID];
|
|
170
|
+
const contentType = contentTypes2[contentTypeUID];
|
|
171
|
+
if (!isEqual(oldContentType?.attributes, contentType?.attributes)) {
|
|
172
|
+
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
173
|
+
where: {
|
|
174
|
+
contentType: contentTypeUID
|
|
175
|
+
},
|
|
176
|
+
populate: {
|
|
177
|
+
entry: true,
|
|
178
|
+
release: true
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
await mapAsync(actions, async (action) => {
|
|
182
|
+
if (action.entry && action.release) {
|
|
183
|
+
const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
|
|
184
|
+
strapi
|
|
185
|
+
});
|
|
186
|
+
if (populatedEntry) {
|
|
187
|
+
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
188
|
+
strapi
|
|
189
|
+
});
|
|
190
|
+
releasesAffected.add(action.release.id);
|
|
191
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
192
|
+
where: {
|
|
193
|
+
id: action.id
|
|
194
|
+
},
|
|
195
|
+
data: {
|
|
196
|
+
isEntryValid
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}).then(() => {
|
|
204
|
+
mapAsync(releasesAffected, async (releaseId) => {
|
|
205
|
+
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async function disableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
211
|
+
if (!oldContentTypes) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
for (const uid in contentTypes2) {
|
|
215
|
+
if (!oldContentTypes[uid]) {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const oldContentType = oldContentTypes[uid];
|
|
219
|
+
const contentType = contentTypes2[uid];
|
|
220
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
221
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
222
|
+
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
223
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
224
|
+
locale: null
|
|
225
|
+
}).where({ contentType: uid }).execute();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function enableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
230
|
+
if (!oldContentTypes) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
for (const uid in contentTypes2) {
|
|
234
|
+
if (!oldContentTypes[uid]) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const oldContentType = oldContentTypes[uid];
|
|
238
|
+
const contentType = contentTypes2[uid];
|
|
239
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
240
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
241
|
+
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
242
|
+
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
243
|
+
const defaultLocale = await getDefaultLocale();
|
|
244
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
245
|
+
locale: defaultLocale
|
|
246
|
+
}).where({ contentType: uid }).execute();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
79
250
|
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
80
251
|
const register = async ({ strapi: strapi2 }) => {
|
|
81
252
|
if (features$2.isEnabled("cms-content-releases")) {
|
|
82
253
|
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
83
|
-
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
|
|
84
|
-
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
|
|
254
|
+
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish).register(disableContentTypeLocalized);
|
|
255
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
256
|
+
}
|
|
257
|
+
if (strapi2.plugin("graphql")) {
|
|
258
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
259
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
260
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
85
261
|
}
|
|
86
|
-
};
|
|
87
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
88
|
-
return strapi2.plugin("content-releases").service(name);
|
|
89
262
|
};
|
|
90
263
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
91
264
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
92
265
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
266
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
267
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
268
|
+
);
|
|
93
269
|
strapi2.db.lifecycles.subscribe({
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
270
|
+
models: contentTypesWithDraftAndPublish,
|
|
271
|
+
async afterDelete(event) {
|
|
272
|
+
try {
|
|
273
|
+
const { model, result } = event;
|
|
274
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
275
|
+
const { id } = result;
|
|
276
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
277
|
+
where: {
|
|
278
|
+
actions: {
|
|
279
|
+
target_type: model.uid,
|
|
280
|
+
target_id: id
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
285
|
+
where: {
|
|
286
|
+
target_type: model.uid,
|
|
287
|
+
target_id: id
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
for (const release2 of releases) {
|
|
291
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
102
292
|
}
|
|
103
|
-
}
|
|
293
|
+
}
|
|
294
|
+
} catch (error) {
|
|
295
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
104
296
|
}
|
|
105
297
|
},
|
|
106
298
|
/**
|
|
@@ -120,18 +312,75 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
120
312
|
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
121
313
|
*/
|
|
122
314
|
async afterDeleteMany(event) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
315
|
+
try {
|
|
316
|
+
const { model, state } = event;
|
|
317
|
+
const entriesToDelete = state.entriesToDelete;
|
|
318
|
+
if (entriesToDelete) {
|
|
319
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
320
|
+
where: {
|
|
321
|
+
actions: {
|
|
322
|
+
target_type: model.uid,
|
|
323
|
+
target_id: {
|
|
324
|
+
$in: entriesToDelete.map(
|
|
325
|
+
(entry) => entry.id
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
}
|
|
131
329
|
}
|
|
330
|
+
});
|
|
331
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
332
|
+
where: {
|
|
333
|
+
target_type: model.uid,
|
|
334
|
+
target_id: {
|
|
335
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
for (const release2 of releases) {
|
|
340
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
132
341
|
}
|
|
342
|
+
}
|
|
343
|
+
} catch (error) {
|
|
344
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
345
|
+
error
|
|
133
346
|
});
|
|
134
347
|
}
|
|
348
|
+
},
|
|
349
|
+
async afterUpdate(event) {
|
|
350
|
+
try {
|
|
351
|
+
const { model, result } = event;
|
|
352
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
353
|
+
const isEntryValid = await getEntryValidStatus(
|
|
354
|
+
model.uid,
|
|
355
|
+
result,
|
|
356
|
+
{
|
|
357
|
+
strapi: strapi2
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
361
|
+
where: {
|
|
362
|
+
target_type: model.uid,
|
|
363
|
+
target_id: result.id
|
|
364
|
+
},
|
|
365
|
+
data: {
|
|
366
|
+
isEntryValid
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
370
|
+
where: {
|
|
371
|
+
actions: {
|
|
372
|
+
target_type: model.uid,
|
|
373
|
+
target_id: result.id
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
for (const release2 of releases) {
|
|
378
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} catch (error) {
|
|
382
|
+
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
383
|
+
}
|
|
135
384
|
}
|
|
136
385
|
});
|
|
137
386
|
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
@@ -141,6 +390,9 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
141
390
|
);
|
|
142
391
|
throw err;
|
|
143
392
|
});
|
|
393
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
394
|
+
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
395
|
+
});
|
|
144
396
|
}
|
|
145
397
|
}
|
|
146
398
|
};
|
|
@@ -183,6 +435,14 @@ const schema$1 = {
|
|
|
183
435
|
scheduledAt: {
|
|
184
436
|
type: "datetime"
|
|
185
437
|
},
|
|
438
|
+
timezone: {
|
|
439
|
+
type: "string"
|
|
440
|
+
},
|
|
441
|
+
status: {
|
|
442
|
+
type: "enumeration",
|
|
443
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
444
|
+
required: true
|
|
445
|
+
},
|
|
186
446
|
actions: {
|
|
187
447
|
type: "relation",
|
|
188
448
|
relation: "oneToMany",
|
|
@@ -235,6 +495,9 @@ const schema = {
|
|
|
235
495
|
relation: "manyToOne",
|
|
236
496
|
target: RELEASE_MODEL_UID,
|
|
237
497
|
inversedBy: "actions"
|
|
498
|
+
},
|
|
499
|
+
isEntryValid: {
|
|
500
|
+
type: "boolean"
|
|
238
501
|
}
|
|
239
502
|
}
|
|
240
503
|
};
|
|
@@ -257,464 +520,563 @@ const getGroupName = (queryValue) => {
|
|
|
257
520
|
return "contentType.displayName";
|
|
258
521
|
}
|
|
259
522
|
};
|
|
260
|
-
const createReleaseService = ({ strapi: strapi2 }) =>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
validateScheduledAtIsLaterThanNow
|
|
267
|
-
} = getService("release-validation", { strapi: strapi2 });
|
|
268
|
-
await Promise.all([
|
|
269
|
-
validatePendingReleasesLimit(),
|
|
270
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
271
|
-
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
272
|
-
]);
|
|
273
|
-
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
274
|
-
data: releaseWithCreatorFields
|
|
523
|
+
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
524
|
+
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
525
|
+
strapi2.eventHub.emit(event, {
|
|
526
|
+
isPublished,
|
|
527
|
+
error,
|
|
528
|
+
release: release2
|
|
275
529
|
});
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
count: true
|
|
530
|
+
};
|
|
531
|
+
return {
|
|
532
|
+
async create(releaseData, { user }) {
|
|
533
|
+
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
534
|
+
const {
|
|
535
|
+
validatePendingReleasesLimit,
|
|
536
|
+
validateUniqueNameForPendingRelease,
|
|
537
|
+
validateScheduledAtIsLaterThanNow
|
|
538
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
539
|
+
await Promise.all([
|
|
540
|
+
validatePendingReleasesLimit(),
|
|
541
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
542
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
543
|
+
]);
|
|
544
|
+
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
545
|
+
data: {
|
|
546
|
+
...releaseWithCreatorFields,
|
|
547
|
+
status: "empty"
|
|
295
548
|
}
|
|
549
|
+
});
|
|
550
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
551
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
552
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
296
553
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
554
|
+
strapi2.telemetry.send("didCreateContentRelease");
|
|
555
|
+
return release2;
|
|
556
|
+
},
|
|
557
|
+
async findOne(id, query = {}) {
|
|
558
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
559
|
+
...query
|
|
560
|
+
});
|
|
561
|
+
return release2;
|
|
562
|
+
},
|
|
563
|
+
findPage(query) {
|
|
564
|
+
return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
|
|
565
|
+
...query,
|
|
566
|
+
populate: {
|
|
567
|
+
actions: {
|
|
568
|
+
// @ts-expect-error Ignore missing properties
|
|
569
|
+
count: true
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
});
|
|
573
|
+
},
|
|
574
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
575
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
576
|
+
where: {
|
|
577
|
+
actions: {
|
|
578
|
+
target_type: contentTypeUid,
|
|
579
|
+
target_id: entryId
|
|
580
|
+
},
|
|
581
|
+
releasedAt: {
|
|
582
|
+
$null: true
|
|
583
|
+
}
|
|
305
584
|
},
|
|
306
|
-
|
|
307
|
-
|
|
585
|
+
populate: {
|
|
586
|
+
// Filter the action to get only the content type entry
|
|
587
|
+
actions: {
|
|
588
|
+
where: {
|
|
589
|
+
target_type: contentTypeUid,
|
|
590
|
+
target_id: entryId
|
|
591
|
+
}
|
|
592
|
+
}
|
|
308
593
|
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
594
|
+
});
|
|
595
|
+
return releases.map((release2) => {
|
|
596
|
+
if (release2.actions?.length) {
|
|
597
|
+
const [actionForEntry] = release2.actions;
|
|
598
|
+
delete release2.actions;
|
|
599
|
+
return {
|
|
600
|
+
...release2,
|
|
601
|
+
action: actionForEntry
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
return release2;
|
|
605
|
+
});
|
|
606
|
+
},
|
|
607
|
+
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
608
|
+
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
609
|
+
where: {
|
|
610
|
+
releasedAt: {
|
|
611
|
+
$null: true
|
|
612
|
+
},
|
|
613
|
+
actions: {
|
|
314
614
|
target_type: contentTypeUid,
|
|
315
615
|
target_id: entryId
|
|
316
616
|
}
|
|
317
617
|
}
|
|
618
|
+
});
|
|
619
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
620
|
+
where: {
|
|
621
|
+
$or: [
|
|
622
|
+
{
|
|
623
|
+
id: {
|
|
624
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
actions: null
|
|
629
|
+
}
|
|
630
|
+
],
|
|
631
|
+
releasedAt: {
|
|
632
|
+
$null: true
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
return releases.map((release2) => {
|
|
637
|
+
if (release2.actions?.length) {
|
|
638
|
+
const [actionForEntry] = release2.actions;
|
|
639
|
+
delete release2.actions;
|
|
640
|
+
return {
|
|
641
|
+
...release2,
|
|
642
|
+
action: actionForEntry
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
return release2;
|
|
646
|
+
});
|
|
647
|
+
},
|
|
648
|
+
async update(id, releaseData, { user }) {
|
|
649
|
+
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(
|
|
650
|
+
releaseData
|
|
651
|
+
);
|
|
652
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
653
|
+
"release-validation",
|
|
654
|
+
{ strapi: strapi2 }
|
|
655
|
+
);
|
|
656
|
+
await Promise.all([
|
|
657
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
658
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
659
|
+
]);
|
|
660
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
661
|
+
if (!release2) {
|
|
662
|
+
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
318
663
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (release2.actions?.length) {
|
|
322
|
-
const [actionForEntry] = release2.actions;
|
|
323
|
-
delete release2.actions;
|
|
324
|
-
return {
|
|
325
|
-
...release2,
|
|
326
|
-
action: actionForEntry
|
|
327
|
-
};
|
|
664
|
+
if (release2.releasedAt) {
|
|
665
|
+
throw new errors.ValidationError("Release already published");
|
|
328
666
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
667
|
+
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
668
|
+
/*
|
|
669
|
+
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
670
|
+
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
671
|
+
*/
|
|
672
|
+
// @ts-expect-error see above
|
|
673
|
+
data: releaseWithCreatorFields
|
|
674
|
+
});
|
|
675
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
676
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
677
|
+
if (releaseData.scheduledAt) {
|
|
678
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
679
|
+
} else if (release2.scheduledAt) {
|
|
680
|
+
schedulingService.cancel(id);
|
|
341
681
|
}
|
|
342
682
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
683
|
+
this.updateReleaseStatus(id);
|
|
684
|
+
strapi2.telemetry.send("didUpdateContentRelease");
|
|
685
|
+
return updatedRelease;
|
|
686
|
+
},
|
|
687
|
+
async createAction(releaseId, action) {
|
|
688
|
+
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
689
|
+
strapi: strapi2
|
|
690
|
+
});
|
|
691
|
+
await Promise.all([
|
|
692
|
+
validateEntryContentType(action.entry.contentType),
|
|
693
|
+
validateUniqueEntry(releaseId, action)
|
|
694
|
+
]);
|
|
695
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
696
|
+
if (!release2) {
|
|
697
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
359
698
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (release2.actions?.length) {
|
|
363
|
-
const [actionForEntry] = release2.actions;
|
|
364
|
-
delete release2.actions;
|
|
365
|
-
return {
|
|
366
|
-
...release2,
|
|
367
|
-
action: actionForEntry
|
|
368
|
-
};
|
|
699
|
+
if (release2.releasedAt) {
|
|
700
|
+
throw new errors.ValidationError("Release already published");
|
|
369
701
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
});
|
|
398
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
399
|
-
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
400
|
-
if (releaseData.scheduledAt) {
|
|
401
|
-
await schedulingService.set(id, releaseData.scheduledAt);
|
|
402
|
-
} else if (release2.scheduledAt) {
|
|
403
|
-
schedulingService.cancel(id);
|
|
702
|
+
const { entry, type } = action;
|
|
703
|
+
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
704
|
+
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
705
|
+
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
706
|
+
data: {
|
|
707
|
+
type,
|
|
708
|
+
contentType: entry.contentType,
|
|
709
|
+
locale: entry.locale,
|
|
710
|
+
isEntryValid,
|
|
711
|
+
entry: {
|
|
712
|
+
id: entry.id,
|
|
713
|
+
__type: entry.contentType,
|
|
714
|
+
__pivot: { field: "entry" }
|
|
715
|
+
},
|
|
716
|
+
release: releaseId
|
|
717
|
+
},
|
|
718
|
+
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
719
|
+
});
|
|
720
|
+
this.updateReleaseStatus(releaseId);
|
|
721
|
+
return releaseAction2;
|
|
722
|
+
},
|
|
723
|
+
async findActions(releaseId, query) {
|
|
724
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
725
|
+
fields: ["id"]
|
|
726
|
+
});
|
|
727
|
+
if (!release2) {
|
|
728
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
404
729
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
});
|
|
412
|
-
await Promise.all([
|
|
413
|
-
validateEntryContentType(action.entry.contentType),
|
|
414
|
-
validateUniqueEntry(releaseId, action)
|
|
415
|
-
]);
|
|
416
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
417
|
-
if (!release2) {
|
|
418
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
419
|
-
}
|
|
420
|
-
if (release2.releasedAt) {
|
|
421
|
-
throw new errors.ValidationError("Release already published");
|
|
422
|
-
}
|
|
423
|
-
const { entry, type } = action;
|
|
424
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
425
|
-
data: {
|
|
426
|
-
type,
|
|
427
|
-
contentType: entry.contentType,
|
|
428
|
-
locale: entry.locale,
|
|
429
|
-
entry: {
|
|
430
|
-
id: entry.id,
|
|
431
|
-
__type: entry.contentType,
|
|
432
|
-
__pivot: { field: "entry" }
|
|
730
|
+
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
731
|
+
...query,
|
|
732
|
+
populate: {
|
|
733
|
+
entry: {
|
|
734
|
+
populate: "*"
|
|
735
|
+
}
|
|
433
736
|
},
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
437
|
-
});
|
|
438
|
-
},
|
|
439
|
-
async findActions(releaseId, query) {
|
|
440
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
441
|
-
fields: ["id"]
|
|
442
|
-
});
|
|
443
|
-
if (!release2) {
|
|
444
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
445
|
-
}
|
|
446
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
447
|
-
...query,
|
|
448
|
-
populate: {
|
|
449
|
-
entry: {
|
|
450
|
-
populate: "*"
|
|
737
|
+
filters: {
|
|
738
|
+
release: releaseId
|
|
451
739
|
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
async groupActions(actions, groupBy) {
|
|
462
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
463
|
-
if (!acc.includes(action.contentType)) {
|
|
464
|
-
acc.push(action.contentType);
|
|
465
|
-
}
|
|
466
|
-
return acc;
|
|
467
|
-
}, []);
|
|
468
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
469
|
-
contentTypeUids
|
|
470
|
-
);
|
|
471
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
472
|
-
const formattedData = actions.map((action) => {
|
|
473
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
474
|
-
return {
|
|
475
|
-
...action,
|
|
476
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
477
|
-
contentType: {
|
|
478
|
-
displayName,
|
|
479
|
-
mainFieldValue: action.entry[mainField],
|
|
480
|
-
uid: action.contentType
|
|
740
|
+
});
|
|
741
|
+
},
|
|
742
|
+
async countActions(query) {
|
|
743
|
+
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
744
|
+
},
|
|
745
|
+
async groupActions(actions, groupBy) {
|
|
746
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
747
|
+
if (!acc.includes(action.contentType)) {
|
|
748
|
+
acc.push(action.contentType);
|
|
481
749
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const contentTypesData = {};
|
|
500
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
501
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
502
|
-
uid: contentTypeUid
|
|
750
|
+
return acc;
|
|
751
|
+
}, []);
|
|
752
|
+
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
753
|
+
contentTypeUids
|
|
754
|
+
);
|
|
755
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
756
|
+
const formattedData = actions.map((action) => {
|
|
757
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
758
|
+
return {
|
|
759
|
+
...action,
|
|
760
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
761
|
+
contentType: {
|
|
762
|
+
displayName,
|
|
763
|
+
mainFieldValue: action.entry[mainField],
|
|
764
|
+
uid: action.contentType
|
|
765
|
+
}
|
|
766
|
+
};
|
|
503
767
|
});
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
},
|
|
511
|
-
getContentTypeModelsFromActions(actions) {
|
|
512
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
513
|
-
if (!acc.includes(action.contentType)) {
|
|
514
|
-
acc.push(action.contentType);
|
|
768
|
+
const groupName = getGroupName(groupBy);
|
|
769
|
+
return _.groupBy(groupName)(formattedData);
|
|
770
|
+
},
|
|
771
|
+
async getLocalesDataForActions() {
|
|
772
|
+
if (!strapi2.plugin("i18n")) {
|
|
773
|
+
return {};
|
|
515
774
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
(acc, contentTypeUid) => {
|
|
520
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
775
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
776
|
+
return allLocales.reduce((acc, locale) => {
|
|
777
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
521
778
|
return acc;
|
|
522
|
-
},
|
|
523
|
-
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
779
|
+
}, {});
|
|
780
|
+
},
|
|
781
|
+
async getContentTypesDataForActions(contentTypesUids) {
|
|
782
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
783
|
+
const contentTypesData = {};
|
|
784
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
785
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
786
|
+
uid: contentTypeUid
|
|
787
|
+
});
|
|
788
|
+
contentTypesData[contentTypeUid] = {
|
|
789
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
790
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
return contentTypesData;
|
|
794
|
+
},
|
|
795
|
+
getContentTypeModelsFromActions(actions) {
|
|
796
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
797
|
+
if (!acc.includes(action.contentType)) {
|
|
798
|
+
acc.push(action.contentType);
|
|
799
|
+
}
|
|
533
800
|
return acc;
|
|
534
|
-
},
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
801
|
+
}, []);
|
|
802
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
803
|
+
(acc, contentTypeUid) => {
|
|
804
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
805
|
+
return acc;
|
|
806
|
+
},
|
|
807
|
+
{}
|
|
808
|
+
);
|
|
809
|
+
return contentTypeModelsMap;
|
|
810
|
+
},
|
|
811
|
+
async getAllComponents() {
|
|
812
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
813
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
814
|
+
const componentsMap = components.reduce(
|
|
815
|
+
(acc, component) => {
|
|
816
|
+
acc[component.uid] = component;
|
|
817
|
+
return acc;
|
|
818
|
+
},
|
|
819
|
+
{}
|
|
820
|
+
);
|
|
821
|
+
return componentsMap;
|
|
822
|
+
},
|
|
823
|
+
async delete(releaseId) {
|
|
824
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
825
|
+
populate: {
|
|
826
|
+
actions: {
|
|
827
|
+
fields: ["id"]
|
|
828
|
+
}
|
|
544
829
|
}
|
|
830
|
+
});
|
|
831
|
+
if (!release2) {
|
|
832
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
545
833
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
where: {
|
|
556
|
-
id: {
|
|
557
|
-
$in: release2.actions.map((action) => action.id)
|
|
834
|
+
if (release2.releasedAt) {
|
|
835
|
+
throw new errors.ValidationError("Release already published");
|
|
836
|
+
}
|
|
837
|
+
await strapi2.db.transaction(async () => {
|
|
838
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
839
|
+
where: {
|
|
840
|
+
id: {
|
|
841
|
+
$in: release2.actions.map((action) => action.id)
|
|
842
|
+
}
|
|
558
843
|
}
|
|
559
|
-
}
|
|
844
|
+
});
|
|
845
|
+
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
560
846
|
});
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
{
|
|
570
|
-
|
|
571
|
-
|
|
847
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
848
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
849
|
+
await schedulingService.cancel(release2.id);
|
|
850
|
+
}
|
|
851
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
852
|
+
return release2;
|
|
853
|
+
},
|
|
854
|
+
async publish(releaseId) {
|
|
855
|
+
try {
|
|
856
|
+
const releaseWithPopulatedActionEntries = await strapi2.entityService.findOne(
|
|
857
|
+
RELEASE_MODEL_UID,
|
|
858
|
+
releaseId,
|
|
859
|
+
{
|
|
572
860
|
populate: {
|
|
573
|
-
|
|
574
|
-
|
|
861
|
+
actions: {
|
|
862
|
+
populate: {
|
|
863
|
+
entry: {
|
|
864
|
+
fields: ["id"]
|
|
865
|
+
}
|
|
866
|
+
}
|
|
575
867
|
}
|
|
576
868
|
}
|
|
577
869
|
}
|
|
870
|
+
);
|
|
871
|
+
if (!releaseWithPopulatedActionEntries) {
|
|
872
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
578
873
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
582
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
583
|
-
}
|
|
584
|
-
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
585
|
-
throw new errors.ValidationError("Release already published");
|
|
586
|
-
}
|
|
587
|
-
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
588
|
-
throw new errors.ValidationError("No entries to publish");
|
|
589
|
-
}
|
|
590
|
-
const collectionTypeActions = {};
|
|
591
|
-
const singleTypeActions = [];
|
|
592
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
593
|
-
const contentTypeUid = action.contentType;
|
|
594
|
-
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
595
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
596
|
-
collectionTypeActions[contentTypeUid] = {
|
|
597
|
-
entriestoPublishIds: [],
|
|
598
|
-
entriesToUnpublishIds: []
|
|
599
|
-
};
|
|
874
|
+
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
875
|
+
throw new errors.ValidationError("Release already published");
|
|
600
876
|
}
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
} else {
|
|
604
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
877
|
+
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
878
|
+
throw new errors.ValidationError("No entries to publish");
|
|
605
879
|
}
|
|
606
|
-
|
|
607
|
-
singleTypeActions
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
await entityManagerService.publish(entry, uid);
|
|
880
|
+
const collectionTypeActions = {};
|
|
881
|
+
const singleTypeActions = [];
|
|
882
|
+
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
883
|
+
const contentTypeUid = action.contentType;
|
|
884
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
885
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
886
|
+
collectionTypeActions[contentTypeUid] = {
|
|
887
|
+
entriestoPublishIds: [],
|
|
888
|
+
entriesToUnpublishIds: []
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
if (action.type === "publish") {
|
|
892
|
+
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
893
|
+
} else {
|
|
894
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
895
|
+
}
|
|
623
896
|
} else {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
;
|
|
629
|
-
else {
|
|
630
|
-
throw error;
|
|
897
|
+
singleTypeActions.push({
|
|
898
|
+
uid: contentTypeUid,
|
|
899
|
+
action: action.type,
|
|
900
|
+
id: action.entry.id
|
|
901
|
+
});
|
|
631
902
|
}
|
|
632
903
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
904
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
905
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
906
|
+
await strapi2.db.transaction(async () => {
|
|
907
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
908
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
909
|
+
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
910
|
+
try {
|
|
911
|
+
if (action === "publish") {
|
|
912
|
+
await entityManagerService.publish(entry, uid);
|
|
913
|
+
} else {
|
|
914
|
+
await entityManagerService.unpublish(entry, uid);
|
|
643
915
|
}
|
|
644
|
-
}
|
|
645
|
-
|
|
916
|
+
} catch (error) {
|
|
917
|
+
if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
918
|
+
} else {
|
|
919
|
+
throw error;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
646
922
|
}
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
923
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
924
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
925
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
926
|
+
const entriesToPublish = await strapi2.entityService.findMany(
|
|
927
|
+
contentTypeUid,
|
|
928
|
+
{
|
|
929
|
+
filters: {
|
|
930
|
+
id: {
|
|
931
|
+
$in: entriestoPublishIds
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
populate
|
|
654
935
|
}
|
|
655
|
-
|
|
656
|
-
|
|
936
|
+
);
|
|
937
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
938
|
+
contentTypeUid,
|
|
939
|
+
{
|
|
940
|
+
filters: {
|
|
941
|
+
id: {
|
|
942
|
+
$in: entriesToUnpublishIds
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
populate
|
|
946
|
+
}
|
|
947
|
+
);
|
|
948
|
+
if (entriesToPublish.length > 0) {
|
|
949
|
+
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
950
|
+
}
|
|
951
|
+
if (entriesToUnpublish.length > 0) {
|
|
952
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
953
|
+
}
|
|
657
954
|
}
|
|
658
|
-
);
|
|
659
|
-
|
|
660
|
-
|
|
955
|
+
});
|
|
956
|
+
const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
|
957
|
+
data: {
|
|
958
|
+
/*
|
|
959
|
+
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
|
960
|
+
*/
|
|
961
|
+
// @ts-expect-error see above
|
|
962
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
963
|
+
},
|
|
964
|
+
populate: {
|
|
965
|
+
actions: {
|
|
966
|
+
// @ts-expect-error is not expecting count but it is working
|
|
967
|
+
count: true
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
972
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
973
|
+
isPublished: true,
|
|
974
|
+
release: release2
|
|
975
|
+
});
|
|
661
976
|
}
|
|
662
|
-
|
|
663
|
-
|
|
977
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
978
|
+
return release2;
|
|
979
|
+
} catch (error) {
|
|
980
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
981
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
982
|
+
isPublished: false,
|
|
983
|
+
error
|
|
984
|
+
});
|
|
664
985
|
}
|
|
986
|
+
strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
987
|
+
where: { id: releaseId },
|
|
988
|
+
data: {
|
|
989
|
+
status: "failed"
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
throw error;
|
|
665
993
|
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
994
|
+
},
|
|
995
|
+
async updateAction(actionId, releaseId, update) {
|
|
996
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
997
|
+
where: {
|
|
998
|
+
id: actionId,
|
|
999
|
+
release: {
|
|
1000
|
+
id: releaseId,
|
|
1001
|
+
releasedAt: {
|
|
1002
|
+
$null: true
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
data: update
|
|
1007
|
+
});
|
|
1008
|
+
if (!updatedAction) {
|
|
1009
|
+
throw new errors.NotFoundError(
|
|
1010
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1011
|
+
);
|
|
674
1012
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
1013
|
+
return updatedAction;
|
|
1014
|
+
},
|
|
1015
|
+
async deleteAction(actionId, releaseId) {
|
|
1016
|
+
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1017
|
+
where: {
|
|
1018
|
+
id: actionId,
|
|
1019
|
+
release: {
|
|
1020
|
+
id: releaseId,
|
|
1021
|
+
releasedAt: {
|
|
1022
|
+
$null: true
|
|
1023
|
+
}
|
|
686
1024
|
}
|
|
687
1025
|
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
);
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
release: {
|
|
703
|
-
id: releaseId,
|
|
704
|
-
releasedAt: {
|
|
705
|
-
$null: true
|
|
1026
|
+
});
|
|
1027
|
+
if (!deletedAction) {
|
|
1028
|
+
throw new errors.NotFoundError(
|
|
1029
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
this.updateReleaseStatus(releaseId);
|
|
1033
|
+
return deletedAction;
|
|
1034
|
+
},
|
|
1035
|
+
async updateReleaseStatus(releaseId) {
|
|
1036
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
1037
|
+
this.countActions({
|
|
1038
|
+
filters: {
|
|
1039
|
+
release: releaseId
|
|
706
1040
|
}
|
|
1041
|
+
}),
|
|
1042
|
+
this.countActions({
|
|
1043
|
+
filters: {
|
|
1044
|
+
release: releaseId,
|
|
1045
|
+
isEntryValid: false
|
|
1046
|
+
}
|
|
1047
|
+
})
|
|
1048
|
+
]);
|
|
1049
|
+
if (totalActions > 0) {
|
|
1050
|
+
if (invalidActions > 0) {
|
|
1051
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1052
|
+
where: {
|
|
1053
|
+
id: releaseId
|
|
1054
|
+
},
|
|
1055
|
+
data: {
|
|
1056
|
+
status: "blocked"
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
707
1059
|
}
|
|
1060
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1061
|
+
where: {
|
|
1062
|
+
id: releaseId
|
|
1063
|
+
},
|
|
1064
|
+
data: {
|
|
1065
|
+
status: "ready"
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
708
1068
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
1069
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1070
|
+
where: {
|
|
1071
|
+
id: releaseId
|
|
1072
|
+
},
|
|
1073
|
+
data: {
|
|
1074
|
+
status: "empty"
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
714
1077
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
});
|
|
1078
|
+
};
|
|
1079
|
+
};
|
|
718
1080
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
719
1081
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
720
1082
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -839,8 +1201,23 @@ const services = {
|
|
|
839
1201
|
};
|
|
840
1202
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
841
1203
|
name: yup.string().trim().required(),
|
|
842
|
-
|
|
843
|
-
|
|
1204
|
+
scheduledAt: yup.string().nullable(),
|
|
1205
|
+
isScheduled: yup.boolean().optional(),
|
|
1206
|
+
time: yup.string().when("isScheduled", {
|
|
1207
|
+
is: true,
|
|
1208
|
+
then: yup.string().trim().required(),
|
|
1209
|
+
otherwise: yup.string().nullable()
|
|
1210
|
+
}),
|
|
1211
|
+
timezone: yup.string().when("isScheduled", {
|
|
1212
|
+
is: true,
|
|
1213
|
+
then: yup.string().required().nullable(),
|
|
1214
|
+
otherwise: yup.string().nullable()
|
|
1215
|
+
}),
|
|
1216
|
+
date: yup.string().when("isScheduled", {
|
|
1217
|
+
is: true,
|
|
1218
|
+
then: yup.string().required().nullable(),
|
|
1219
|
+
otherwise: yup.string().nullable()
|
|
1220
|
+
})
|
|
844
1221
|
}).required().noUnknown();
|
|
845
1222
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
846
1223
|
const releaseController = {
|
|
@@ -873,7 +1250,12 @@ const releaseController = {
|
|
|
873
1250
|
}
|
|
874
1251
|
};
|
|
875
1252
|
});
|
|
876
|
-
|
|
1253
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1254
|
+
where: {
|
|
1255
|
+
releasedAt: null
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
877
1259
|
}
|
|
878
1260
|
},
|
|
879
1261
|
async findOne(ctx) {
|
|
@@ -1244,6 +1626,9 @@ const getPlugin = () => {
|
|
|
1244
1626
|
};
|
|
1245
1627
|
}
|
|
1246
1628
|
return {
|
|
1629
|
+
// Always return register, it handles its own feature check
|
|
1630
|
+
register,
|
|
1631
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1247
1632
|
contentTypes
|
|
1248
1633
|
};
|
|
1249
1634
|
};
|