@strapi/content-releases 0.0.0-next.6d59515520a3850456f256fb0e4c54b75054ddf4 → 0.0.0-next.898f8ae81b2cb3f89bd012e9db20a2d9b78a48d2
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-_20W9dYa.js → App-OK4Xac-O.js} +518 -240
- package/dist/_chunks/App-OK4Xac-O.js.map +1 -0
- package/dist/_chunks/{App-L1jSxCiL.mjs → App-xAkiD42p.mjs} +522 -245
- package/dist/_chunks/App-xAkiD42p.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
- package/dist/_chunks/{en-gYDqKYFd.js → en-r0otWaln.js} +18 -4
- package/dist/_chunks/en-r0otWaln.js.map +1 -0
- package/dist/_chunks/{en-MyLPoISH.mjs → en-veqvqeEr.mjs} +18 -4
- package/dist/_chunks/en-veqvqeEr.mjs.map +1 -0
- package/dist/_chunks/{index-KJa1Rb5F.js → index-JvA2_26n.js} +134 -27
- package/dist/_chunks/index-JvA2_26n.js.map +1 -0
- package/dist/_chunks/{index-c4zRX_sg.mjs → index-exoiSU3V.mjs} +139 -32
- package/dist/_chunks/index-exoiSU3V.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +2 -2
- package/dist/server/index.js +597 -382
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +597 -382
- package/dist/server/index.mjs.map +1 -1
- package/package.json +12 -9
- package/dist/_chunks/App-L1jSxCiL.mjs.map +0 -1
- package/dist/_chunks/App-_20W9dYa.js.map +0 -1
- package/dist/_chunks/en-MyLPoISH.mjs.map +0 -1
- package/dist/_chunks/en-gYDqKYFd.js.map +0 -1
- package/dist/_chunks/index-KJa1Rb5F.js.map +0 -1
- package/dist/_chunks/index-c4zRX_sg.mjs.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -3,6 +3,7 @@ const utils = require("@strapi/utils");
|
|
|
3
3
|
const lodash = require("lodash");
|
|
4
4
|
const _ = require("lodash/fp");
|
|
5
5
|
const EE = require("@strapi/strapi/dist/utils/ee");
|
|
6
|
+
const nodeSchedule = require("node-schedule");
|
|
6
7
|
const yup = require("yup");
|
|
7
8
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
8
9
|
function _interopNamespace(e) {
|
|
@@ -72,6 +73,9 @@ const ACTIONS = [
|
|
|
72
73
|
pluginName: "content-releases"
|
|
73
74
|
}
|
|
74
75
|
];
|
|
76
|
+
const ALLOWED_WEBHOOK_EVENTS = {
|
|
77
|
+
RELEASES_PUBLISH: "releases.publish"
|
|
78
|
+
};
|
|
75
79
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
76
80
|
oldContentTypes,
|
|
77
81
|
contentTypes: contentTypes2
|
|
@@ -106,6 +110,9 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
106
110
|
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
|
|
107
111
|
}
|
|
108
112
|
};
|
|
113
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
114
|
+
return strapi2.plugin("content-releases").service(name);
|
|
115
|
+
};
|
|
109
116
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
110
117
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
111
118
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
@@ -153,6 +160,27 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
153
160
|
}
|
|
154
161
|
}
|
|
155
162
|
});
|
|
163
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
164
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
165
|
+
strapi2.log.error(
|
|
166
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
167
|
+
);
|
|
168
|
+
throw err;
|
|
169
|
+
});
|
|
170
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
171
|
+
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const destroy = async ({ strapi: strapi2 }) => {
|
|
177
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
178
|
+
const scheduledJobs = getService("scheduling", {
|
|
179
|
+
strapi: strapi2
|
|
180
|
+
}).getAll();
|
|
181
|
+
for (const [, job] of scheduledJobs) {
|
|
182
|
+
job.cancel();
|
|
183
|
+
}
|
|
156
184
|
}
|
|
157
185
|
};
|
|
158
186
|
const schema$1 = {
|
|
@@ -181,6 +209,12 @@ const schema$1 = {
|
|
|
181
209
|
releasedAt: {
|
|
182
210
|
type: "datetime"
|
|
183
211
|
},
|
|
212
|
+
scheduledAt: {
|
|
213
|
+
type: "datetime"
|
|
214
|
+
},
|
|
215
|
+
timezone: {
|
|
216
|
+
type: "string"
|
|
217
|
+
},
|
|
184
218
|
actions: {
|
|
185
219
|
type: "relation",
|
|
186
220
|
relation: "oneToMany",
|
|
@@ -243,9 +277,6 @@ const contentTypes = {
|
|
|
243
277
|
release: release$1,
|
|
244
278
|
"release-action": releaseAction$1
|
|
245
279
|
};
|
|
246
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
247
|
-
return strapi2.plugin("content-releases").service(name);
|
|
248
|
-
};
|
|
249
280
|
const getGroupName = (queryValue) => {
|
|
250
281
|
switch (queryValue) {
|
|
251
282
|
case "contentType":
|
|
@@ -258,415 +289,504 @@ const getGroupName = (queryValue) => {
|
|
|
258
289
|
return "contentType.displayName";
|
|
259
290
|
}
|
|
260
291
|
};
|
|
261
|
-
const createReleaseService = ({ strapi: strapi2 }) =>
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
);
|
|
268
|
-
await Promise.all([
|
|
269
|
-
validatePendingReleasesLimit(),
|
|
270
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
|
|
271
|
-
]);
|
|
272
|
-
return strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
273
|
-
data: releaseWithCreatorFields
|
|
292
|
+
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
293
|
+
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
294
|
+
strapi2.eventHub.emit(event, {
|
|
295
|
+
isPublished,
|
|
296
|
+
error,
|
|
297
|
+
release: release2
|
|
274
298
|
});
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
299
|
+
};
|
|
300
|
+
return {
|
|
301
|
+
async create(releaseData, { user }) {
|
|
302
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
303
|
+
const {
|
|
304
|
+
validatePendingReleasesLimit,
|
|
305
|
+
validateUniqueNameForPendingRelease,
|
|
306
|
+
validateScheduledAtIsLaterThanNow
|
|
307
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
308
|
+
await Promise.all([
|
|
309
|
+
validatePendingReleasesLimit(),
|
|
310
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
311
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
312
|
+
]);
|
|
313
|
+
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
314
|
+
data: releaseWithCreatorFields
|
|
315
|
+
});
|
|
316
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
317
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
318
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
290
319
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
320
|
+
strapi2.telemetry.send("didCreateContentRelease");
|
|
321
|
+
return release2;
|
|
322
|
+
},
|
|
323
|
+
async findOne(id, query = {}) {
|
|
324
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
325
|
+
...query
|
|
326
|
+
});
|
|
327
|
+
return release2;
|
|
328
|
+
},
|
|
329
|
+
findPage(query) {
|
|
330
|
+
return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
|
|
331
|
+
...query,
|
|
332
|
+
populate: {
|
|
333
|
+
actions: {
|
|
334
|
+
// @ts-expect-error Ignore missing properties
|
|
335
|
+
count: true
|
|
336
|
+
}
|
|
302
337
|
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
338
|
+
});
|
|
339
|
+
},
|
|
340
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
341
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
342
|
+
where: {
|
|
343
|
+
actions: {
|
|
308
344
|
target_type: contentTypeUid,
|
|
309
345
|
target_id: entryId
|
|
346
|
+
},
|
|
347
|
+
releasedAt: {
|
|
348
|
+
$null: true
|
|
310
349
|
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
});
|
|
314
|
-
return releases.map((release2) => {
|
|
315
|
-
if (release2.actions?.length) {
|
|
316
|
-
const [actionForEntry] = release2.actions;
|
|
317
|
-
delete release2.actions;
|
|
318
|
-
return {
|
|
319
|
-
...release2,
|
|
320
|
-
action: actionForEntry
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
return release2;
|
|
324
|
-
});
|
|
325
|
-
},
|
|
326
|
-
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
327
|
-
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
328
|
-
where: {
|
|
329
|
-
releasedAt: {
|
|
330
|
-
$null: true
|
|
331
350
|
},
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
339
|
-
where: {
|
|
340
|
-
$or: [
|
|
341
|
-
{
|
|
342
|
-
id: {
|
|
343
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
351
|
+
populate: {
|
|
352
|
+
// Filter the action to get only the content type entry
|
|
353
|
+
actions: {
|
|
354
|
+
where: {
|
|
355
|
+
target_type: contentTypeUid,
|
|
356
|
+
target_id: entryId
|
|
344
357
|
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
return releases.map((release2) => {
|
|
362
|
+
if (release2.actions?.length) {
|
|
363
|
+
const [actionForEntry] = release2.actions;
|
|
364
|
+
delete release2.actions;
|
|
365
|
+
return {
|
|
366
|
+
...release2,
|
|
367
|
+
action: actionForEntry
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
return release2;
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
374
|
+
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
375
|
+
where: {
|
|
376
|
+
releasedAt: {
|
|
377
|
+
$null: true
|
|
345
378
|
},
|
|
346
|
-
{
|
|
347
|
-
|
|
379
|
+
actions: {
|
|
380
|
+
target_type: contentTypeUid,
|
|
381
|
+
target_id: entryId
|
|
348
382
|
}
|
|
349
|
-
],
|
|
350
|
-
releasedAt: {
|
|
351
|
-
$null: true
|
|
352
383
|
}
|
|
384
|
+
});
|
|
385
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
386
|
+
where: {
|
|
387
|
+
$or: [
|
|
388
|
+
{
|
|
389
|
+
id: {
|
|
390
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
actions: null
|
|
395
|
+
}
|
|
396
|
+
],
|
|
397
|
+
releasedAt: {
|
|
398
|
+
$null: true
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
return releases.map((release2) => {
|
|
403
|
+
if (release2.actions?.length) {
|
|
404
|
+
const [actionForEntry] = release2.actions;
|
|
405
|
+
delete release2.actions;
|
|
406
|
+
return {
|
|
407
|
+
...release2,
|
|
408
|
+
action: actionForEntry
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
return release2;
|
|
412
|
+
});
|
|
413
|
+
},
|
|
414
|
+
async update(id, releaseData, { user }) {
|
|
415
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(
|
|
416
|
+
releaseData
|
|
417
|
+
);
|
|
418
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
419
|
+
"release-validation",
|
|
420
|
+
{ strapi: strapi2 }
|
|
421
|
+
);
|
|
422
|
+
await Promise.all([
|
|
423
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
424
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
425
|
+
]);
|
|
426
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
427
|
+
if (!release2) {
|
|
428
|
+
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
353
429
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (release2.actions?.length) {
|
|
357
|
-
const [actionForEntry] = release2.actions;
|
|
358
|
-
delete release2.actions;
|
|
359
|
-
return {
|
|
360
|
-
...release2,
|
|
361
|
-
action: actionForEntry
|
|
362
|
-
};
|
|
430
|
+
if (release2.releasedAt) {
|
|
431
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
363
432
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
379
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
380
|
-
*/
|
|
381
|
-
// @ts-expect-error see above
|
|
382
|
-
data: releaseWithCreatorFields
|
|
383
|
-
});
|
|
384
|
-
return updatedRelease;
|
|
385
|
-
},
|
|
386
|
-
async createAction(releaseId, action) {
|
|
387
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
388
|
-
strapi: strapi2
|
|
389
|
-
});
|
|
390
|
-
await Promise.all([
|
|
391
|
-
validateEntryContentType(action.entry.contentType),
|
|
392
|
-
validateUniqueEntry(releaseId, action)
|
|
393
|
-
]);
|
|
394
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
395
|
-
if (!release2) {
|
|
396
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
397
|
-
}
|
|
398
|
-
if (release2.releasedAt) {
|
|
399
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
400
|
-
}
|
|
401
|
-
const { entry, type } = action;
|
|
402
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
403
|
-
data: {
|
|
404
|
-
type,
|
|
405
|
-
contentType: entry.contentType,
|
|
406
|
-
locale: entry.locale,
|
|
407
|
-
entry: {
|
|
408
|
-
id: entry.id,
|
|
409
|
-
__type: entry.contentType,
|
|
410
|
-
__pivot: { field: "entry" }
|
|
411
|
-
},
|
|
412
|
-
release: releaseId
|
|
413
|
-
},
|
|
414
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
415
|
-
});
|
|
416
|
-
},
|
|
417
|
-
async findActions(releaseId, query) {
|
|
418
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
419
|
-
fields: ["id"]
|
|
420
|
-
});
|
|
421
|
-
if (!release2) {
|
|
422
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
423
|
-
}
|
|
424
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
425
|
-
...query,
|
|
426
|
-
populate: {
|
|
427
|
-
entry: {
|
|
428
|
-
populate: "*"
|
|
433
|
+
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
434
|
+
/*
|
|
435
|
+
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
436
|
+
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
437
|
+
*/
|
|
438
|
+
// @ts-expect-error see above
|
|
439
|
+
data: releaseWithCreatorFields
|
|
440
|
+
});
|
|
441
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
442
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
443
|
+
if (releaseData.scheduledAt) {
|
|
444
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
445
|
+
} else if (release2.scheduledAt) {
|
|
446
|
+
schedulingService.cancel(id);
|
|
429
447
|
}
|
|
430
|
-
},
|
|
431
|
-
filters: {
|
|
432
|
-
release: releaseId
|
|
433
448
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
449
|
+
strapi2.telemetry.send("didUpdateContentRelease");
|
|
450
|
+
return updatedRelease;
|
|
451
|
+
},
|
|
452
|
+
async createAction(releaseId, action) {
|
|
453
|
+
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
454
|
+
strapi: strapi2
|
|
455
|
+
});
|
|
456
|
+
await Promise.all([
|
|
457
|
+
validateEntryContentType(action.entry.contentType),
|
|
458
|
+
validateUniqueEntry(releaseId, action)
|
|
459
|
+
]);
|
|
460
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
461
|
+
if (!release2) {
|
|
462
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
443
463
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
});
|
|
462
|
-
const groupName = getGroupName(groupBy);
|
|
463
|
-
return ___default.default.groupBy(groupName)(formattedData);
|
|
464
|
-
},
|
|
465
|
-
async getLocalesDataForActions() {
|
|
466
|
-
if (!strapi2.plugin("i18n")) {
|
|
467
|
-
return {};
|
|
468
|
-
}
|
|
469
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
470
|
-
return allLocales.reduce((acc, locale) => {
|
|
471
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
472
|
-
return acc;
|
|
473
|
-
}, {});
|
|
474
|
-
},
|
|
475
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
476
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
477
|
-
const contentTypesData = {};
|
|
478
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
479
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
480
|
-
uid: contentTypeUid
|
|
464
|
+
if (release2.releasedAt) {
|
|
465
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
466
|
+
}
|
|
467
|
+
const { entry, type } = action;
|
|
468
|
+
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
469
|
+
data: {
|
|
470
|
+
type,
|
|
471
|
+
contentType: entry.contentType,
|
|
472
|
+
locale: entry.locale,
|
|
473
|
+
entry: {
|
|
474
|
+
id: entry.id,
|
|
475
|
+
__type: entry.contentType,
|
|
476
|
+
__pivot: { field: "entry" }
|
|
477
|
+
},
|
|
478
|
+
release: releaseId
|
|
479
|
+
},
|
|
480
|
+
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
481
481
|
});
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
getContentTypeModelsFromActions(actions) {
|
|
490
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
491
|
-
if (!acc.includes(action.contentType)) {
|
|
492
|
-
acc.push(action.contentType);
|
|
482
|
+
},
|
|
483
|
+
async findActions(releaseId, query) {
|
|
484
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
485
|
+
fields: ["id"]
|
|
486
|
+
});
|
|
487
|
+
if (!release2) {
|
|
488
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
493
489
|
}
|
|
494
|
-
return
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
490
|
+
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
491
|
+
...query,
|
|
492
|
+
populate: {
|
|
493
|
+
entry: {
|
|
494
|
+
populate: "*"
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
filters: {
|
|
498
|
+
release: releaseId
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
},
|
|
502
|
+
async countActions(query) {
|
|
503
|
+
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
504
|
+
},
|
|
505
|
+
async groupActions(actions, groupBy) {
|
|
506
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
507
|
+
if (!acc.includes(action.contentType)) {
|
|
508
|
+
acc.push(action.contentType);
|
|
509
|
+
}
|
|
499
510
|
return acc;
|
|
500
|
-
},
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
+
}, []);
|
|
512
|
+
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
513
|
+
contentTypeUids
|
|
514
|
+
);
|
|
515
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
516
|
+
const formattedData = actions.map((action) => {
|
|
517
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
518
|
+
return {
|
|
519
|
+
...action,
|
|
520
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
521
|
+
contentType: {
|
|
522
|
+
displayName,
|
|
523
|
+
mainFieldValue: action.entry[mainField],
|
|
524
|
+
uid: action.contentType
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
});
|
|
528
|
+
const groupName = getGroupName(groupBy);
|
|
529
|
+
return ___default.default.groupBy(groupName)(formattedData);
|
|
530
|
+
},
|
|
531
|
+
async getLocalesDataForActions() {
|
|
532
|
+
if (!strapi2.plugin("i18n")) {
|
|
533
|
+
return {};
|
|
534
|
+
}
|
|
535
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
536
|
+
return allLocales.reduce((acc, locale) => {
|
|
537
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
511
538
|
return acc;
|
|
512
|
-
},
|
|
513
|
-
|
|
514
|
-
)
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
539
|
+
}, {});
|
|
540
|
+
},
|
|
541
|
+
async getContentTypesDataForActions(contentTypesUids) {
|
|
542
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
543
|
+
const contentTypesData = {};
|
|
544
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
545
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
546
|
+
uid: contentTypeUid
|
|
547
|
+
});
|
|
548
|
+
contentTypesData[contentTypeUid] = {
|
|
549
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
550
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
551
|
+
};
|
|
523
552
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
await strapi2.db.transaction(async () => {
|
|
532
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
533
|
-
where: {
|
|
534
|
-
id: {
|
|
535
|
-
$in: release2.actions.map((action) => action.id)
|
|
536
|
-
}
|
|
553
|
+
return contentTypesData;
|
|
554
|
+
},
|
|
555
|
+
getContentTypeModelsFromActions(actions) {
|
|
556
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
557
|
+
if (!acc.includes(action.contentType)) {
|
|
558
|
+
acc.push(action.contentType);
|
|
537
559
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
560
|
+
return acc;
|
|
561
|
+
}, []);
|
|
562
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
563
|
+
(acc, contentTypeUid) => {
|
|
564
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
565
|
+
return acc;
|
|
566
|
+
},
|
|
567
|
+
{}
|
|
568
|
+
);
|
|
569
|
+
return contentTypeModelsMap;
|
|
570
|
+
},
|
|
571
|
+
async getAllComponents() {
|
|
572
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
573
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
574
|
+
const componentsMap = components.reduce(
|
|
575
|
+
(acc, component) => {
|
|
576
|
+
acc[component.uid] = component;
|
|
577
|
+
return acc;
|
|
578
|
+
},
|
|
579
|
+
{}
|
|
580
|
+
);
|
|
581
|
+
return componentsMap;
|
|
582
|
+
},
|
|
583
|
+
async delete(releaseId) {
|
|
584
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
548
585
|
populate: {
|
|
549
586
|
actions: {
|
|
550
|
-
|
|
551
|
-
entry: {
|
|
552
|
-
fields: ["id"]
|
|
553
|
-
}
|
|
554
|
-
}
|
|
587
|
+
fields: ["id"]
|
|
555
588
|
}
|
|
556
589
|
}
|
|
590
|
+
});
|
|
591
|
+
if (!release2) {
|
|
592
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
557
593
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
561
|
-
}
|
|
562
|
-
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
563
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
564
|
-
}
|
|
565
|
-
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
566
|
-
throw new utils.errors.ValidationError("No entries to publish");
|
|
567
|
-
}
|
|
568
|
-
const actions = {};
|
|
569
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
570
|
-
const contentTypeUid = action.contentType;
|
|
571
|
-
if (!actions[contentTypeUid]) {
|
|
572
|
-
actions[contentTypeUid] = {
|
|
573
|
-
entriestoPublishIds: [],
|
|
574
|
-
entriesToUnpublishIds: []
|
|
575
|
-
};
|
|
594
|
+
if (release2.releasedAt) {
|
|
595
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
576
596
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
597
|
+
await strapi2.db.transaction(async () => {
|
|
598
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
599
|
+
where: {
|
|
600
|
+
id: {
|
|
601
|
+
$in: release2.actions.map((action) => action.id)
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
606
|
+
});
|
|
607
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
608
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
609
|
+
await schedulingService.cancel(release2.id);
|
|
581
610
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
contentTypeUid,
|
|
611
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
612
|
+
return release2;
|
|
613
|
+
},
|
|
614
|
+
async publish(releaseId) {
|
|
615
|
+
try {
|
|
616
|
+
const releaseWithPopulatedActionEntries = await strapi2.entityService.findOne(
|
|
617
|
+
RELEASE_MODEL_UID,
|
|
618
|
+
releaseId,
|
|
591
619
|
{
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
620
|
+
populate: {
|
|
621
|
+
actions: {
|
|
622
|
+
populate: {
|
|
623
|
+
entry: {
|
|
624
|
+
fields: ["id"]
|
|
625
|
+
}
|
|
626
|
+
}
|
|
595
627
|
}
|
|
596
|
-
}
|
|
597
|
-
populate
|
|
628
|
+
}
|
|
598
629
|
}
|
|
599
630
|
);
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
631
|
+
if (!releaseWithPopulatedActionEntries) {
|
|
632
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
633
|
+
}
|
|
634
|
+
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
635
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
636
|
+
}
|
|
637
|
+
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
638
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
639
|
+
}
|
|
640
|
+
const collectionTypeActions = {};
|
|
641
|
+
const singleTypeActions = [];
|
|
642
|
+
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
643
|
+
const contentTypeUid = action.contentType;
|
|
644
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
645
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
646
|
+
collectionTypeActions[contentTypeUid] = {
|
|
647
|
+
entriestoPublishIds: [],
|
|
648
|
+
entriesToUnpublishIds: []
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
if (action.type === "publish") {
|
|
652
|
+
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
653
|
+
} else {
|
|
654
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
655
|
+
}
|
|
656
|
+
} else {
|
|
657
|
+
singleTypeActions.push({
|
|
658
|
+
uid: contentTypeUid,
|
|
659
|
+
action: action.type,
|
|
660
|
+
id: action.entry.id
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
665
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
666
|
+
await strapi2.db.transaction(async () => {
|
|
667
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
668
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
669
|
+
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
670
|
+
try {
|
|
671
|
+
if (action === "publish") {
|
|
672
|
+
await entityManagerService.publish(entry, uid);
|
|
673
|
+
} else {
|
|
674
|
+
await entityManagerService.unpublish(entry, uid);
|
|
606
675
|
}
|
|
607
|
-
}
|
|
608
|
-
|
|
676
|
+
} catch (error) {
|
|
677
|
+
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
678
|
+
} else {
|
|
679
|
+
throw error;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
609
682
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
683
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
684
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
685
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
686
|
+
const entriesToPublish = await strapi2.entityService.findMany(
|
|
687
|
+
contentTypeUid,
|
|
688
|
+
{
|
|
689
|
+
filters: {
|
|
690
|
+
id: {
|
|
691
|
+
$in: entriestoPublishIds
|
|
692
|
+
}
|
|
693
|
+
},
|
|
694
|
+
populate
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
698
|
+
contentTypeUid,
|
|
699
|
+
{
|
|
700
|
+
filters: {
|
|
701
|
+
id: {
|
|
702
|
+
$in: entriesToUnpublishIds
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
populate
|
|
706
|
+
}
|
|
707
|
+
);
|
|
708
|
+
if (entriesToPublish.length > 0) {
|
|
709
|
+
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
710
|
+
}
|
|
711
|
+
if (entriesToUnpublish.length > 0) {
|
|
712
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
});
|
|
716
|
+
const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
|
717
|
+
data: {
|
|
718
|
+
/*
|
|
719
|
+
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
|
720
|
+
*/
|
|
721
|
+
// @ts-expect-error see above
|
|
722
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
723
|
+
},
|
|
724
|
+
populate: {
|
|
725
|
+
actions: {
|
|
726
|
+
// @ts-expect-error is not expecting count but it is working
|
|
727
|
+
count: true
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
732
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
733
|
+
isPublished: true,
|
|
734
|
+
release: release2
|
|
735
|
+
});
|
|
613
736
|
}
|
|
614
|
-
|
|
615
|
-
|
|
737
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
738
|
+
return release2;
|
|
739
|
+
} catch (error) {
|
|
740
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
741
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
742
|
+
isPublished: false,
|
|
743
|
+
error
|
|
744
|
+
});
|
|
616
745
|
}
|
|
746
|
+
throw error;
|
|
617
747
|
}
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
return release2;
|
|
629
|
-
},
|
|
630
|
-
async updateAction(actionId, releaseId, update) {
|
|
631
|
-
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
632
|
-
where: {
|
|
633
|
-
id: actionId,
|
|
634
|
-
release: {
|
|
635
|
-
id: releaseId,
|
|
636
|
-
releasedAt: {
|
|
637
|
-
$null: true
|
|
748
|
+
},
|
|
749
|
+
async updateAction(actionId, releaseId, update) {
|
|
750
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
751
|
+
where: {
|
|
752
|
+
id: actionId,
|
|
753
|
+
release: {
|
|
754
|
+
id: releaseId,
|
|
755
|
+
releasedAt: {
|
|
756
|
+
$null: true
|
|
757
|
+
}
|
|
638
758
|
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
759
|
+
},
|
|
760
|
+
data: update
|
|
761
|
+
});
|
|
762
|
+
if (!updatedAction) {
|
|
763
|
+
throw new utils.errors.NotFoundError(
|
|
764
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
return updatedAction;
|
|
768
|
+
},
|
|
769
|
+
async deleteAction(actionId, releaseId) {
|
|
770
|
+
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
771
|
+
where: {
|
|
772
|
+
id: actionId,
|
|
773
|
+
release: {
|
|
774
|
+
id: releaseId,
|
|
775
|
+
releasedAt: {
|
|
776
|
+
$null: true
|
|
777
|
+
}
|
|
658
778
|
}
|
|
659
779
|
}
|
|
780
|
+
});
|
|
781
|
+
if (!deletedAction) {
|
|
782
|
+
throw new utils.errors.NotFoundError(
|
|
783
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
784
|
+
);
|
|
660
785
|
}
|
|
661
|
-
|
|
662
|
-
if (!deletedAction) {
|
|
663
|
-
throw new utils.errors.NotFoundError(
|
|
664
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
665
|
-
);
|
|
786
|
+
return deletedAction;
|
|
666
787
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
});
|
|
788
|
+
};
|
|
789
|
+
};
|
|
670
790
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
671
791
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
672
792
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -711,27 +831,103 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
711
831
|
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
712
832
|
}
|
|
713
833
|
},
|
|
714
|
-
async validateUniqueNameForPendingRelease(name) {
|
|
834
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
715
835
|
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
716
836
|
filters: {
|
|
717
837
|
releasedAt: {
|
|
718
838
|
$null: true
|
|
719
839
|
},
|
|
720
|
-
name
|
|
840
|
+
name,
|
|
841
|
+
...id && { id: { $ne: id } }
|
|
721
842
|
}
|
|
722
843
|
});
|
|
723
844
|
const isNameUnique = pendingReleases.length === 0;
|
|
724
845
|
if (!isNameUnique) {
|
|
725
846
|
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
726
847
|
}
|
|
848
|
+
},
|
|
849
|
+
async validateScheduledAtIsLaterThanNow(scheduledAt) {
|
|
850
|
+
if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
|
|
851
|
+
throw new utils.errors.ValidationError("Scheduled at must be later than now");
|
|
852
|
+
}
|
|
727
853
|
}
|
|
728
854
|
});
|
|
855
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
856
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
857
|
+
return {
|
|
858
|
+
async set(releaseId, scheduleDate) {
|
|
859
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
|
|
860
|
+
if (!release2) {
|
|
861
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
862
|
+
}
|
|
863
|
+
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
864
|
+
try {
|
|
865
|
+
await getService("release").publish(releaseId);
|
|
866
|
+
} catch (error) {
|
|
867
|
+
}
|
|
868
|
+
this.cancel(releaseId);
|
|
869
|
+
});
|
|
870
|
+
if (scheduledJobs.has(releaseId)) {
|
|
871
|
+
this.cancel(releaseId);
|
|
872
|
+
}
|
|
873
|
+
scheduledJobs.set(releaseId, job);
|
|
874
|
+
return scheduledJobs;
|
|
875
|
+
},
|
|
876
|
+
cancel(releaseId) {
|
|
877
|
+
if (scheduledJobs.has(releaseId)) {
|
|
878
|
+
scheduledJobs.get(releaseId).cancel();
|
|
879
|
+
scheduledJobs.delete(releaseId);
|
|
880
|
+
}
|
|
881
|
+
return scheduledJobs;
|
|
882
|
+
},
|
|
883
|
+
getAll() {
|
|
884
|
+
return scheduledJobs;
|
|
885
|
+
},
|
|
886
|
+
/**
|
|
887
|
+
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
888
|
+
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
889
|
+
* This also could be used to sync different Strapi instances in case of a cluster
|
|
890
|
+
*/
|
|
891
|
+
async syncFromDatabase() {
|
|
892
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
893
|
+
where: {
|
|
894
|
+
scheduledAt: {
|
|
895
|
+
$gte: /* @__PURE__ */ new Date()
|
|
896
|
+
},
|
|
897
|
+
releasedAt: null
|
|
898
|
+
}
|
|
899
|
+
});
|
|
900
|
+
for (const release2 of releases) {
|
|
901
|
+
this.set(release2.id, release2.scheduledAt);
|
|
902
|
+
}
|
|
903
|
+
return scheduledJobs;
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
};
|
|
729
907
|
const services = {
|
|
730
908
|
release: createReleaseService,
|
|
731
|
-
"release-validation": createReleaseValidationService
|
|
909
|
+
"release-validation": createReleaseValidationService,
|
|
910
|
+
...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
|
|
732
911
|
};
|
|
733
912
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
734
|
-
name: yup__namespace.string().trim().required()
|
|
913
|
+
name: yup__namespace.string().trim().required(),
|
|
914
|
+
scheduledAt: yup__namespace.string().nullable(),
|
|
915
|
+
isScheduled: yup__namespace.boolean().optional(),
|
|
916
|
+
time: yup__namespace.string().when("isScheduled", {
|
|
917
|
+
is: true,
|
|
918
|
+
then: yup__namespace.string().trim().required(),
|
|
919
|
+
otherwise: yup__namespace.string().nullable()
|
|
920
|
+
}),
|
|
921
|
+
timezone: yup__namespace.string().when("isScheduled", {
|
|
922
|
+
is: true,
|
|
923
|
+
then: yup__namespace.string().required().nullable(),
|
|
924
|
+
otherwise: yup__namespace.string().nullable()
|
|
925
|
+
}),
|
|
926
|
+
date: yup__namespace.string().when("isScheduled", {
|
|
927
|
+
is: true,
|
|
928
|
+
then: yup__namespace.string().required().nullable(),
|
|
929
|
+
otherwise: yup__namespace.string().nullable()
|
|
930
|
+
})
|
|
735
931
|
}).required().noUnknown();
|
|
736
932
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
737
933
|
const releaseController = {
|
|
@@ -771,19 +967,18 @@ const releaseController = {
|
|
|
771
967
|
const id = ctx.params.id;
|
|
772
968
|
const releaseService = getService("release", { strapi });
|
|
773
969
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
});
|
|
778
|
-
const sanitizedRelease = await permissionsManager.sanitizeOutput(release2);
|
|
970
|
+
if (!release2) {
|
|
971
|
+
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
972
|
+
}
|
|
779
973
|
const count = await releaseService.countActions({
|
|
780
974
|
filters: {
|
|
781
975
|
release: id
|
|
782
976
|
}
|
|
783
977
|
});
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
978
|
+
const sanitizedRelease = {
|
|
979
|
+
...release2,
|
|
980
|
+
createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
|
|
981
|
+
};
|
|
787
982
|
const data = {
|
|
788
983
|
...sanitizedRelease,
|
|
789
984
|
actions: {
|
|
@@ -836,8 +1031,27 @@ const releaseController = {
|
|
|
836
1031
|
const id = ctx.params.id;
|
|
837
1032
|
const releaseService = getService("release", { strapi });
|
|
838
1033
|
const release2 = await releaseService.publish(id, { user });
|
|
1034
|
+
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1035
|
+
releaseService.countActions({
|
|
1036
|
+
filters: {
|
|
1037
|
+
release: id,
|
|
1038
|
+
type: "publish"
|
|
1039
|
+
}
|
|
1040
|
+
}),
|
|
1041
|
+
releaseService.countActions({
|
|
1042
|
+
filters: {
|
|
1043
|
+
release: id,
|
|
1044
|
+
type: "unpublish"
|
|
1045
|
+
}
|
|
1046
|
+
})
|
|
1047
|
+
]);
|
|
839
1048
|
ctx.body = {
|
|
840
|
-
data: release2
|
|
1049
|
+
data: release2,
|
|
1050
|
+
meta: {
|
|
1051
|
+
totalEntries: countPublishActions + countUnpublishActions,
|
|
1052
|
+
totalPublishedEntries: countPublishActions,
|
|
1053
|
+
totalUnpublishedEntries: countUnpublishActions
|
|
1054
|
+
}
|
|
841
1055
|
};
|
|
842
1056
|
}
|
|
843
1057
|
};
|
|
@@ -1109,6 +1323,7 @@ const getPlugin = () => {
|
|
|
1109
1323
|
return {
|
|
1110
1324
|
register,
|
|
1111
1325
|
bootstrap,
|
|
1326
|
+
destroy,
|
|
1112
1327
|
contentTypes,
|
|
1113
1328
|
services,
|
|
1114
1329
|
controllers,
|