@strapi/content-releases 0.0.0-next.95a939e004e74915357523e3adb118a31fef57ed → 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-U6GbyLIE.mjs → App-bpzO2Ljh.mjs} +613 -367
- package/dist/_chunks/App-bpzO2Ljh.mjs.map +1 -0
- package/dist/_chunks/{App-1hHIqUoZ.js → App-p8aKBitd.js} +603 -356
- package/dist/_chunks/App-p8aKBitd.js.map +1 -0
- package/dist/_chunks/{en-GqXgfmzl.mjs → en-WuuhP6Bn.mjs} +13 -4
- package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
- package/dist/_chunks/{en-bDhIlw-B.js → en-gcJJ5htG.js} +13 -4
- package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
- package/dist/_chunks/{index-gkExFBa0.mjs → index-AECgcaDa.mjs} +85 -22
- package/dist/_chunks/index-AECgcaDa.mjs.map +1 -0
- package/dist/_chunks/{index-l-FvkQlQ.js → index-fP3qoWZ4.js} +74 -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 +812 -434
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +811 -434
- package/dist/server/index.mjs.map +1 -1
- package/package.json +13 -11
- package/dist/_chunks/App-1hHIqUoZ.js.map +0 -1
- package/dist/_chunks/App-U6GbyLIE.mjs.map +0 -1
- package/dist/_chunks/en-GqXgfmzl.mjs.map +0 -1
- package/dist/_chunks/en-bDhIlw-B.js.map +0 -1
- package/dist/_chunks/index-gkExFBa0.mjs.map +0 -1
- package/dist/_chunks/index-l-FvkQlQ.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
|
};
|
|
@@ -186,6 +438,11 @@ const schema$1 = {
|
|
|
186
438
|
timezone: {
|
|
187
439
|
type: "string"
|
|
188
440
|
},
|
|
441
|
+
status: {
|
|
442
|
+
type: "enumeration",
|
|
443
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
444
|
+
required: true
|
|
445
|
+
},
|
|
189
446
|
actions: {
|
|
190
447
|
type: "relation",
|
|
191
448
|
relation: "oneToMany",
|
|
@@ -238,6 +495,9 @@ const schema = {
|
|
|
238
495
|
relation: "manyToOne",
|
|
239
496
|
target: RELEASE_MODEL_UID,
|
|
240
497
|
inversedBy: "actions"
|
|
498
|
+
},
|
|
499
|
+
isEntryValid: {
|
|
500
|
+
type: "boolean"
|
|
241
501
|
}
|
|
242
502
|
}
|
|
243
503
|
};
|
|
@@ -260,464 +520,563 @@ const getGroupName = (queryValue) => {
|
|
|
260
520
|
return "contentType.displayName";
|
|
261
521
|
}
|
|
262
522
|
};
|
|
263
|
-
const createReleaseService = ({ strapi: strapi2 }) =>
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
validateScheduledAtIsLaterThanNow
|
|
270
|
-
} = getService("release-validation", { strapi: strapi2 });
|
|
271
|
-
await Promise.all([
|
|
272
|
-
validatePendingReleasesLimit(),
|
|
273
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
274
|
-
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
275
|
-
]);
|
|
276
|
-
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
277
|
-
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
|
|
278
529
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
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"
|
|
298
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);
|
|
299
553
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
+
}
|
|
308
584
|
},
|
|
309
|
-
|
|
310
|
-
|
|
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
|
+
}
|
|
311
593
|
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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: {
|
|
317
614
|
target_type: contentTypeUid,
|
|
318
615
|
target_id: entryId
|
|
319
616
|
}
|
|
320
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}`);
|
|
321
663
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (release2.actions?.length) {
|
|
325
|
-
const [actionForEntry] = release2.actions;
|
|
326
|
-
delete release2.actions;
|
|
327
|
-
return {
|
|
328
|
-
...release2,
|
|
329
|
-
action: actionForEntry
|
|
330
|
-
};
|
|
664
|
+
if (release2.releasedAt) {
|
|
665
|
+
throw new errors.ValidationError("Release already published");
|
|
331
666
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
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);
|
|
344
681
|
}
|
|
345
682
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
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}`);
|
|
362
698
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (release2.actions?.length) {
|
|
366
|
-
const [actionForEntry] = release2.actions;
|
|
367
|
-
delete release2.actions;
|
|
368
|
-
return {
|
|
369
|
-
...release2,
|
|
370
|
-
action: actionForEntry
|
|
371
|
-
};
|
|
699
|
+
if (release2.releasedAt) {
|
|
700
|
+
throw new errors.ValidationError("Release already published");
|
|
372
701
|
}
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
});
|
|
401
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
402
|
-
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
403
|
-
if (releaseData.scheduledAt) {
|
|
404
|
-
await schedulingService.set(id, releaseData.scheduledAt);
|
|
405
|
-
} else if (release2.scheduledAt) {
|
|
406
|
-
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}`);
|
|
407
729
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
});
|
|
415
|
-
await Promise.all([
|
|
416
|
-
validateEntryContentType(action.entry.contentType),
|
|
417
|
-
validateUniqueEntry(releaseId, action)
|
|
418
|
-
]);
|
|
419
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
420
|
-
if (!release2) {
|
|
421
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
422
|
-
}
|
|
423
|
-
if (release2.releasedAt) {
|
|
424
|
-
throw new errors.ValidationError("Release already published");
|
|
425
|
-
}
|
|
426
|
-
const { entry, type } = action;
|
|
427
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
428
|
-
data: {
|
|
429
|
-
type,
|
|
430
|
-
contentType: entry.contentType,
|
|
431
|
-
locale: entry.locale,
|
|
432
|
-
entry: {
|
|
433
|
-
id: entry.id,
|
|
434
|
-
__type: entry.contentType,
|
|
435
|
-
__pivot: { field: "entry" }
|
|
730
|
+
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
731
|
+
...query,
|
|
732
|
+
populate: {
|
|
733
|
+
entry: {
|
|
734
|
+
populate: "*"
|
|
735
|
+
}
|
|
436
736
|
},
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
440
|
-
});
|
|
441
|
-
},
|
|
442
|
-
async findActions(releaseId, query) {
|
|
443
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
444
|
-
fields: ["id"]
|
|
445
|
-
});
|
|
446
|
-
if (!release2) {
|
|
447
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
448
|
-
}
|
|
449
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
450
|
-
...query,
|
|
451
|
-
populate: {
|
|
452
|
-
entry: {
|
|
453
|
-
populate: "*"
|
|
737
|
+
filters: {
|
|
738
|
+
release: releaseId
|
|
454
739
|
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
async groupActions(actions, groupBy) {
|
|
465
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
466
|
-
if (!acc.includes(action.contentType)) {
|
|
467
|
-
acc.push(action.contentType);
|
|
468
|
-
}
|
|
469
|
-
return acc;
|
|
470
|
-
}, []);
|
|
471
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
472
|
-
contentTypeUids
|
|
473
|
-
);
|
|
474
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
475
|
-
const formattedData = actions.map((action) => {
|
|
476
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
477
|
-
return {
|
|
478
|
-
...action,
|
|
479
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
480
|
-
contentType: {
|
|
481
|
-
displayName,
|
|
482
|
-
mainFieldValue: action.entry[mainField],
|
|
483
|
-
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);
|
|
484
749
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const contentTypesData = {};
|
|
503
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
504
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
505
|
-
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
|
+
};
|
|
506
767
|
});
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
},
|
|
514
|
-
getContentTypeModelsFromActions(actions) {
|
|
515
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
516
|
-
if (!acc.includes(action.contentType)) {
|
|
517
|
-
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 {};
|
|
518
774
|
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
(acc, contentTypeUid) => {
|
|
523
|
-
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 };
|
|
524
778
|
return acc;
|
|
525
|
-
},
|
|
526
|
-
|
|
527
|
-
)
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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
|
+
}
|
|
536
800
|
return acc;
|
|
537
|
-
},
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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
|
+
}
|
|
547
829
|
}
|
|
830
|
+
});
|
|
831
|
+
if (!release2) {
|
|
832
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
548
833
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
where: {
|
|
559
|
-
id: {
|
|
560
|
-
$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
|
+
}
|
|
561
843
|
}
|
|
562
|
-
}
|
|
844
|
+
});
|
|
845
|
+
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
563
846
|
});
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
{
|
|
573
|
-
|
|
574
|
-
|
|
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
|
+
{
|
|
575
860
|
populate: {
|
|
576
|
-
|
|
577
|
-
|
|
861
|
+
actions: {
|
|
862
|
+
populate: {
|
|
863
|
+
entry: {
|
|
864
|
+
fields: ["id"]
|
|
865
|
+
}
|
|
866
|
+
}
|
|
578
867
|
}
|
|
579
868
|
}
|
|
580
869
|
}
|
|
870
|
+
);
|
|
871
|
+
if (!releaseWithPopulatedActionEntries) {
|
|
872
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
581
873
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
585
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
586
|
-
}
|
|
587
|
-
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
588
|
-
throw new errors.ValidationError("Release already published");
|
|
589
|
-
}
|
|
590
|
-
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
591
|
-
throw new errors.ValidationError("No entries to publish");
|
|
592
|
-
}
|
|
593
|
-
const collectionTypeActions = {};
|
|
594
|
-
const singleTypeActions = [];
|
|
595
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
596
|
-
const contentTypeUid = action.contentType;
|
|
597
|
-
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
598
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
599
|
-
collectionTypeActions[contentTypeUid] = {
|
|
600
|
-
entriestoPublishIds: [],
|
|
601
|
-
entriesToUnpublishIds: []
|
|
602
|
-
};
|
|
874
|
+
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
875
|
+
throw new errors.ValidationError("Release already published");
|
|
603
876
|
}
|
|
604
|
-
if (
|
|
605
|
-
|
|
606
|
-
} else {
|
|
607
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
877
|
+
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
878
|
+
throw new errors.ValidationError("No entries to publish");
|
|
608
879
|
}
|
|
609
|
-
|
|
610
|
-
singleTypeActions
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
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
|
+
}
|
|
626
896
|
} else {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
;
|
|
632
|
-
else {
|
|
633
|
-
throw error;
|
|
897
|
+
singleTypeActions.push({
|
|
898
|
+
uid: contentTypeUid,
|
|
899
|
+
action: action.type,
|
|
900
|
+
id: action.entry.id
|
|
901
|
+
});
|
|
634
902
|
}
|
|
635
903
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
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);
|
|
646
915
|
}
|
|
647
|
-
}
|
|
648
|
-
|
|
916
|
+
} catch (error) {
|
|
917
|
+
if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
918
|
+
} else {
|
|
919
|
+
throw error;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
649
922
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
|
657
935
|
}
|
|
658
|
-
|
|
659
|
-
|
|
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
|
+
}
|
|
660
954
|
}
|
|
661
|
-
);
|
|
662
|
-
|
|
663
|
-
|
|
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
|
+
});
|
|
664
976
|
}
|
|
665
|
-
|
|
666
|
-
|
|
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
|
+
});
|
|
667
985
|
}
|
|
986
|
+
strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
987
|
+
where: { id: releaseId },
|
|
988
|
+
data: {
|
|
989
|
+
status: "failed"
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
throw error;
|
|
668
993
|
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
+
);
|
|
677
1012
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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
|
+
}
|
|
689
1024
|
}
|
|
690
1025
|
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
release: {
|
|
706
|
-
id: releaseId,
|
|
707
|
-
releasedAt: {
|
|
708
|
-
$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
|
|
709
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
|
+
});
|
|
710
1059
|
}
|
|
1060
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1061
|
+
where: {
|
|
1062
|
+
id: releaseId
|
|
1063
|
+
},
|
|
1064
|
+
data: {
|
|
1065
|
+
status: "ready"
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
711
1068
|
}
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1069
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1070
|
+
where: {
|
|
1071
|
+
id: releaseId
|
|
1072
|
+
},
|
|
1073
|
+
data: {
|
|
1074
|
+
status: "empty"
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
717
1077
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
});
|
|
1078
|
+
};
|
|
1079
|
+
};
|
|
721
1080
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
722
1081
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
723
1082
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -842,11 +1201,21 @@ const services = {
|
|
|
842
1201
|
};
|
|
843
1202
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
844
1203
|
name: yup.string().trim().required(),
|
|
845
|
-
// scheduledAt is a date, but we always receive strings from the client
|
|
846
1204
|
scheduledAt: yup.string().nullable(),
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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(),
|
|
850
1219
|
otherwise: yup.string().nullable()
|
|
851
1220
|
})
|
|
852
1221
|
}).required().noUnknown();
|
|
@@ -881,7 +1250,12 @@ const releaseController = {
|
|
|
881
1250
|
}
|
|
882
1251
|
};
|
|
883
1252
|
});
|
|
884
|
-
|
|
1253
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1254
|
+
where: {
|
|
1255
|
+
releasedAt: null
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
885
1259
|
}
|
|
886
1260
|
},
|
|
887
1261
|
async findOne(ctx) {
|
|
@@ -1252,6 +1626,9 @@ const getPlugin = () => {
|
|
|
1252
1626
|
};
|
|
1253
1627
|
}
|
|
1254
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
|
|
1255
1632
|
contentTypes
|
|
1256
1633
|
};
|
|
1257
1634
|
};
|