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