@strapi/content-releases 0.0.0-next.44f19b3d2f81d983c343a219aa2781ee0deecb5f → 0.0.0-next.4af8963f6880c5fb9fae32ecd580f5cd33eaddda
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-3ycH2d3s.mjs → App-ise7GunC.mjs} +365 -154
- package/dist/_chunks/App-ise7GunC.mjs.map +1 -0
- package/dist/_chunks/{App-5PsAyVt2.js → App-w2Zq-wj5.js} +363 -151
- package/dist/_chunks/App-w2Zq-wj5.js.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
- package/dist/_chunks/{en-2DuPv5k0.js → en-7P4i1cWH.js} +11 -3
- package/dist/_chunks/en-7P4i1cWH.js.map +1 -0
- package/dist/_chunks/{en-SOqjCdyh.mjs → en-pb1wUzhy.mjs} +11 -3
- package/dist/_chunks/en-pb1wUzhy.mjs.map +1 -0
- package/dist/_chunks/{index-4gUWuCQV.mjs → index-D-Yjf60c.mjs} +57 -16
- package/dist/_chunks/index-D-Yjf60c.mjs.map +1 -0
- package/dist/_chunks/{index-D57Rztnc.js → index-Q8Pv7enO.js} +57 -16
- package/dist/_chunks/index-Q8Pv7enO.js.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +573 -403
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +573 -403
- package/dist/server/index.mjs.map +1 -1
- package/package.json +12 -9
- package/dist/_chunks/App-3ycH2d3s.mjs.map +0 -1
- package/dist/_chunks/App-5PsAyVt2.js.map +0 -1
- package/dist/_chunks/en-2DuPv5k0.js.map +0 -1
- package/dist/_chunks/en-SOqjCdyh.mjs.map +0 -1
- package/dist/_chunks/index-4gUWuCQV.mjs.map +0 -1
- package/dist/_chunks/index-D57Rztnc.js.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,441 +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
|
|
382
|
+
}
|
|
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
|
|
348
399
|
}
|
|
349
|
-
],
|
|
350
|
-
releasedAt: {
|
|
351
|
-
$null: true
|
|
352
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
|
-
|
|
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
|
+
});
|
|
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}`);
|
|
489
|
+
}
|
|
490
|
+
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
491
|
+
...query,
|
|
492
|
+
populate: {
|
|
493
|
+
entry: {
|
|
494
|
+
populate: "*"
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
filters: {
|
|
498
|
+
release: releaseId
|
|
459
499
|
}
|
|
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
|
|
481
500
|
});
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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
|
+
}
|
|
510
|
+
return acc;
|
|
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 {};
|
|
493
534
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
(acc, contentTypeUid) => {
|
|
498
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
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 };
|
|
499
538
|
return acc;
|
|
500
|
-
},
|
|
501
|
-
|
|
502
|
-
)
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
+
};
|
|
552
|
+
}
|
|
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);
|
|
559
|
+
}
|
|
511
560
|
return acc;
|
|
512
|
-
},
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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, {
|
|
585
|
+
populate: {
|
|
586
|
+
actions: {
|
|
587
|
+
fields: ["id"]
|
|
588
|
+
}
|
|
522
589
|
}
|
|
590
|
+
});
|
|
591
|
+
if (!release2) {
|
|
592
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
523
593
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
where: {
|
|
534
|
-
id: {
|
|
535
|
-
$in: release2.actions.map((action) => action.id)
|
|
594
|
+
if (release2.releasedAt) {
|
|
595
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
596
|
+
}
|
|
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
|
+
}
|
|
536
603
|
}
|
|
537
|
-
}
|
|
604
|
+
});
|
|
605
|
+
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
538
606
|
});
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
{
|
|
548
|
-
|
|
549
|
-
|
|
607
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
608
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
609
|
+
await schedulingService.cancel(release2.id);
|
|
610
|
+
}
|
|
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,
|
|
619
|
+
{
|
|
550
620
|
populate: {
|
|
551
|
-
|
|
552
|
-
|
|
621
|
+
actions: {
|
|
622
|
+
populate: {
|
|
623
|
+
entry: {
|
|
624
|
+
fields: ["id"]
|
|
625
|
+
}
|
|
626
|
+
}
|
|
553
627
|
}
|
|
554
628
|
}
|
|
555
629
|
}
|
|
630
|
+
);
|
|
631
|
+
if (!releaseWithPopulatedActionEntries) {
|
|
632
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
556
633
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
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 collectionTypeActions = {};
|
|
569
|
-
const singleTypeActions = [];
|
|
570
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
571
|
-
const contentTypeUid = action.contentType;
|
|
572
|
-
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
573
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
574
|
-
collectionTypeActions[contentTypeUid] = {
|
|
575
|
-
entriestoPublishIds: [],
|
|
576
|
-
entriesToUnpublishIds: []
|
|
577
|
-
};
|
|
634
|
+
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
635
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
578
636
|
}
|
|
579
|
-
if (
|
|
580
|
-
|
|
581
|
-
} else {
|
|
582
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
637
|
+
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
638
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
583
639
|
}
|
|
584
|
-
|
|
585
|
-
singleTypeActions
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
await entityManagerService.publish(entry, uid);
|
|
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
|
+
}
|
|
601
656
|
} else {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
;
|
|
607
|
-
else {
|
|
608
|
-
throw error;
|
|
657
|
+
singleTypeActions.push({
|
|
658
|
+
uid: contentTypeUid,
|
|
659
|
+
action: action.type,
|
|
660
|
+
id: action.entry.id
|
|
661
|
+
});
|
|
609
662
|
}
|
|
610
663
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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);
|
|
621
675
|
}
|
|
622
|
-
}
|
|
623
|
-
|
|
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
|
+
}
|
|
624
682
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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
|
|
632
695
|
}
|
|
633
|
-
|
|
634
|
-
|
|
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
|
+
}
|
|
635
714
|
}
|
|
636
|
-
);
|
|
637
|
-
|
|
638
|
-
|
|
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
|
+
});
|
|
639
736
|
}
|
|
640
|
-
|
|
641
|
-
|
|
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
|
+
});
|
|
642
745
|
}
|
|
746
|
+
throw error;
|
|
643
747
|
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
return release2;
|
|
655
|
-
},
|
|
656
|
-
async updateAction(actionId, releaseId, update) {
|
|
657
|
-
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
658
|
-
where: {
|
|
659
|
-
id: actionId,
|
|
660
|
-
release: {
|
|
661
|
-
id: releaseId,
|
|
662
|
-
releasedAt: {
|
|
663
|
-
$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
|
+
}
|
|
664
758
|
}
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
+
}
|
|
684
778
|
}
|
|
685
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
|
+
);
|
|
686
785
|
}
|
|
687
|
-
|
|
688
|
-
if (!deletedAction) {
|
|
689
|
-
throw new utils.errors.NotFoundError(
|
|
690
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
691
|
-
);
|
|
786
|
+
return deletedAction;
|
|
692
787
|
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
});
|
|
788
|
+
};
|
|
789
|
+
};
|
|
696
790
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
697
791
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
698
792
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -737,27 +831,103 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
737
831
|
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
738
832
|
}
|
|
739
833
|
},
|
|
740
|
-
async validateUniqueNameForPendingRelease(name) {
|
|
834
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
741
835
|
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
742
836
|
filters: {
|
|
743
837
|
releasedAt: {
|
|
744
838
|
$null: true
|
|
745
839
|
},
|
|
746
|
-
name
|
|
840
|
+
name,
|
|
841
|
+
...id && { id: { $ne: id } }
|
|
747
842
|
}
|
|
748
843
|
});
|
|
749
844
|
const isNameUnique = pendingReleases.length === 0;
|
|
750
845
|
if (!isNameUnique) {
|
|
751
846
|
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
752
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
|
+
}
|
|
753
853
|
}
|
|
754
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
|
+
};
|
|
755
907
|
const services = {
|
|
756
908
|
release: createReleaseService,
|
|
757
|
-
"release-validation": createReleaseValidationService
|
|
909
|
+
"release-validation": createReleaseValidationService,
|
|
910
|
+
...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
|
|
758
911
|
};
|
|
759
912
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
760
|
-
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
|
+
})
|
|
761
931
|
}).required().noUnknown();
|
|
762
932
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
763
933
|
const releaseController = {
|
|
@@ -797,19 +967,18 @@ const releaseController = {
|
|
|
797
967
|
const id = ctx.params.id;
|
|
798
968
|
const releaseService = getService("release", { strapi });
|
|
799
969
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
});
|
|
804
|
-
const sanitizedRelease = await permissionsManager.sanitizeOutput(release2);
|
|
970
|
+
if (!release2) {
|
|
971
|
+
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
972
|
+
}
|
|
805
973
|
const count = await releaseService.countActions({
|
|
806
974
|
filters: {
|
|
807
975
|
release: id
|
|
808
976
|
}
|
|
809
977
|
});
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
978
|
+
const sanitizedRelease = {
|
|
979
|
+
...release2,
|
|
980
|
+
createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
|
|
981
|
+
};
|
|
813
982
|
const data = {
|
|
814
983
|
...sanitizedRelease,
|
|
815
984
|
actions: {
|
|
@@ -1154,6 +1323,7 @@ const getPlugin = () => {
|
|
|
1154
1323
|
return {
|
|
1155
1324
|
register,
|
|
1156
1325
|
bootstrap,
|
|
1326
|
+
destroy,
|
|
1157
1327
|
contentTypes,
|
|
1158
1328
|
services,
|
|
1159
1329
|
controllers,
|