@strapi/content-releases 5.0.0-beta.8 → 5.0.0-rc.0

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.
Files changed (98) hide show
  1. package/LICENSE +17 -1
  2. package/dist/_chunks/{App-D_6Y9N2F.mjs → App-6gbM0io-.mjs} +215 -255
  3. package/dist/_chunks/App-6gbM0io-.mjs.map +1 -0
  4. package/dist/_chunks/{App-DUmziQ17.js → App-zzUitryC.js} +216 -257
  5. package/dist/_chunks/App-zzUitryC.js.map +1 -0
  6. package/dist/_chunks/SettingsPage-CTOl3Qtw.js +157 -0
  7. package/dist/_chunks/SettingsPage-CTOl3Qtw.js.map +1 -0
  8. package/dist/_chunks/SettingsPage-CuSH193e.mjs +157 -0
  9. package/dist/_chunks/SettingsPage-CuSH193e.mjs.map +1 -0
  10. package/dist/_chunks/{en-DtFJ5ViE.js → en-1_1re1mc.js} +7 -2
  11. package/dist/_chunks/en-1_1re1mc.js.map +1 -0
  12. package/dist/_chunks/{en-B9Ur3VsE.mjs → en-C3sq5KNP.mjs} +7 -2
  13. package/dist/_chunks/en-C3sq5KNP.mjs.map +1 -0
  14. package/dist/_chunks/{index-C5Hc767q.js → index-BxRiXenX.js} +518 -437
  15. package/dist/_chunks/index-BxRiXenX.js.map +1 -0
  16. package/dist/_chunks/{index-BomF0-yY.mjs → index-CuchqQ4_.mjs} +525 -444
  17. package/dist/_chunks/index-CuchqQ4_.mjs.map +1 -0
  18. package/dist/_chunks/validation-schemas-C7P2rhPu.mjs +29 -0
  19. package/dist/_chunks/validation-schemas-C7P2rhPu.mjs.map +1 -0
  20. package/dist/_chunks/validation-schemas-bib1fBc7.js +47 -0
  21. package/dist/_chunks/validation-schemas-bib1fBc7.js.map +1 -0
  22. package/dist/admin/index.js +1 -1
  23. package/dist/admin/index.mjs +2 -2
  24. package/dist/admin/src/components/ReleaseActionMenu.d.ts +2 -2
  25. package/dist/admin/src/components/{CMReleasesContainer.d.ts → ReleaseActionModal.d.ts} +3 -1
  26. package/dist/admin/src/components/ReleaseModal.d.ts +2 -1
  27. package/dist/admin/src/components/ReleasesPanel.d.ts +3 -0
  28. package/dist/admin/src/pages/SettingsPage.d.ts +1 -0
  29. package/dist/admin/src/services/release.d.ts +37 -32
  30. package/dist/admin/src/utils/time.d.ts +9 -0
  31. package/dist/server/index.js +784 -600
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/server/index.mjs +785 -601
  34. package/dist/server/index.mjs.map +1 -1
  35. package/dist/server/src/bootstrap.d.ts.map +1 -1
  36. package/dist/server/src/constants.d.ts +11 -2
  37. package/dist/server/src/constants.d.ts.map +1 -1
  38. package/dist/server/src/content-types/index.d.ts +3 -5
  39. package/dist/server/src/content-types/index.d.ts.map +1 -1
  40. package/dist/server/src/content-types/release-action/index.d.ts +3 -5
  41. package/dist/server/src/content-types/release-action/index.d.ts.map +1 -1
  42. package/dist/server/src/content-types/release-action/schema.d.ts +3 -5
  43. package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -1
  44. package/dist/server/src/controllers/index.d.ts +6 -3
  45. package/dist/server/src/controllers/index.d.ts.map +1 -1
  46. package/dist/server/src/controllers/release-action.d.ts +0 -1
  47. package/dist/server/src/controllers/release-action.d.ts.map +1 -1
  48. package/dist/server/src/controllers/release.d.ts +7 -2
  49. package/dist/server/src/controllers/release.d.ts.map +1 -1
  50. package/dist/server/src/controllers/settings.d.ts +11 -0
  51. package/dist/server/src/controllers/settings.d.ts.map +1 -0
  52. package/dist/server/src/controllers/validation/release-action.d.ts +7 -1
  53. package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -1
  54. package/dist/server/src/controllers/validation/release.d.ts +1 -0
  55. package/dist/server/src/controllers/validation/release.d.ts.map +1 -1
  56. package/dist/server/src/controllers/validation/settings.d.ts +2 -0
  57. package/dist/server/src/controllers/validation/settings.d.ts.map +1 -0
  58. package/dist/server/src/index.d.ts +66 -51
  59. package/dist/server/src/index.d.ts.map +1 -1
  60. package/dist/server/src/middlewares/documents.d.ts +6 -0
  61. package/dist/server/src/middlewares/documents.d.ts.map +1 -0
  62. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts +9 -0
  63. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts.map +1 -0
  64. package/dist/server/src/migrations/index.d.ts.map +1 -1
  65. package/dist/server/src/register.d.ts.map +1 -1
  66. package/dist/server/src/routes/index.d.ts +16 -0
  67. package/dist/server/src/routes/index.d.ts.map +1 -1
  68. package/dist/server/src/routes/release-action.d.ts.map +1 -1
  69. package/dist/server/src/routes/release.d.ts.map +1 -1
  70. package/dist/server/src/routes/settings.d.ts +18 -0
  71. package/dist/server/src/routes/settings.d.ts.map +1 -0
  72. package/dist/server/src/services/index.d.ts +38 -38
  73. package/dist/server/src/services/index.d.ts.map +1 -1
  74. package/dist/server/src/services/release-action.d.ts +36 -0
  75. package/dist/server/src/services/release-action.d.ts.map +1 -0
  76. package/dist/server/src/services/release.d.ts +6 -41
  77. package/dist/server/src/services/release.d.ts.map +1 -1
  78. package/dist/server/src/services/settings.d.ts +13 -0
  79. package/dist/server/src/services/settings.d.ts.map +1 -0
  80. package/dist/server/src/services/validation.d.ts +1 -1
  81. package/dist/server/src/services/validation.d.ts.map +1 -1
  82. package/dist/server/src/utils/index.d.ts +29 -8
  83. package/dist/server/src/utils/index.d.ts.map +1 -1
  84. package/dist/shared/contracts/release-actions.d.ts +6 -5
  85. package/dist/shared/contracts/release-actions.d.ts.map +1 -1
  86. package/dist/shared/contracts/releases.d.ts +6 -5
  87. package/dist/shared/contracts/releases.d.ts.map +1 -1
  88. package/dist/shared/contracts/settings.d.ts +39 -0
  89. package/dist/shared/contracts/settings.d.ts.map +1 -0
  90. package/dist/shared/validation-schemas.d.ts +1 -0
  91. package/dist/shared/validation-schemas.d.ts.map +1 -1
  92. package/package.json +10 -9
  93. package/dist/_chunks/App-DUmziQ17.js.map +0 -1
  94. package/dist/_chunks/App-D_6Y9N2F.mjs.map +0 -1
  95. package/dist/_chunks/en-B9Ur3VsE.mjs.map +0 -1
  96. package/dist/_chunks/en-DtFJ5ViE.js.map +0 -1
  97. package/dist/_chunks/index-BomF0-yY.mjs.map +0 -1
  98. package/dist/_chunks/index-C5Hc767q.js.map +0 -1
@@ -71,6 +71,23 @@ const ACTIONS = [
71
71
  displayName: "Add an entry to a release",
72
72
  uid: "create-action",
73
73
  pluginName: "content-releases"
74
+ },
75
+ // Settings
76
+ {
77
+ uid: "settings.read",
78
+ section: "settings",
79
+ displayName: "Read",
80
+ category: "content releases",
81
+ subCategory: "options",
82
+ pluginName: "content-releases"
83
+ },
84
+ {
85
+ uid: "settings.update",
86
+ section: "settings",
87
+ displayName: "Edit",
88
+ category: "content releases",
89
+ subCategory: "options",
90
+ pluginName: "content-releases"
74
91
  }
75
92
  ];
76
93
  const ALLOWED_WEBHOOK_EVENTS = {
@@ -79,16 +96,13 @@ const ALLOWED_WEBHOOK_EVENTS = {
79
96
  const getService = (name, { strapi: strapi2 }) => {
80
97
  return strapi2.plugin("content-releases").service(name);
81
98
  };
82
- const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 }) => {
99
+ const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
83
100
  const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
84
- const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
85
- const entry = await strapi2.db.query(contentTypeUid).findOne({
86
- where: { id: entryId },
87
- populate
88
- });
89
- return entry;
101
+ const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
102
+ const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
103
+ return isEntryValid(contentType, entry, { strapi: strapi2 });
90
104
  };
91
- const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 }) => {
105
+ const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
92
106
  try {
93
107
  await strapi2.entityValidator.validateEntityCreation(
94
108
  strapi2.getModel(contentTypeUid),
@@ -102,6 +116,38 @@ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 }) =
102
116
  return false;
103
117
  }
104
118
  };
119
+ const getEntry = async ({
120
+ contentType,
121
+ documentId,
122
+ locale,
123
+ populate,
124
+ status = "draft"
125
+ }, { strapi: strapi2 }) => {
126
+ if (documentId) {
127
+ return strapi2.documents(contentType).findOne({ documentId, locale, populate, status });
128
+ }
129
+ return strapi2.documents(contentType).findFirst({ locale, populate, status });
130
+ };
131
+ const getEntryStatus = async (contentType, entry) => {
132
+ if (entry.publishedAt) {
133
+ return "published";
134
+ }
135
+ const publishedEntry = await strapi.documents(contentType).findOne({
136
+ documentId: entry.documentId,
137
+ locale: entry.locale,
138
+ status: "published",
139
+ fields: ["updatedAt"]
140
+ });
141
+ if (!publishedEntry) {
142
+ return "draft";
143
+ }
144
+ const entryUpdatedAt = new Date(entry.updatedAt).getTime();
145
+ const publishedEntryUpdatedAt = new Date(publishedEntry.updatedAt).getTime();
146
+ if (entryUpdatedAt > publishedEntryUpdatedAt) {
147
+ return "modified";
148
+ }
149
+ return "published";
150
+ };
105
151
  async function deleteActionsOnDisableDraftAndPublish({
106
152
  oldContentTypes,
107
153
  contentTypes: contentTypes2
@@ -147,20 +193,22 @@ async function migrateIsValidAndStatusReleases() {
147
193
  const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
148
194
  for (const action of notValidatedActions) {
149
195
  if (action.entry) {
150
- const populatedEntry = await getPopulatedEntry(action.contentType, action.entry.id, {
151
- strapi
196
+ const isEntryValid2 = getDraftEntryValidStatus(
197
+ {
198
+ contentType: action.contentType,
199
+ documentId: action.entryDocumentId,
200
+ locale: action.locale
201
+ },
202
+ { strapi }
203
+ );
204
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
205
+ where: {
206
+ id: action.id
207
+ },
208
+ data: {
209
+ isEntryValid: isEntryValid2
210
+ }
152
211
  });
153
- if (populatedEntry) {
154
- const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
155
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
156
- where: {
157
- id: action.id
158
- },
159
- data: {
160
- isEntryValid
161
- }
162
- });
163
- }
164
212
  }
165
213
  }
166
214
  return getService("release", { strapi }).updateReleaseStatus(release2.id);
@@ -204,24 +252,24 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
204
252
  }
205
253
  });
206
254
  await utils.async.map(actions, async (action) => {
207
- if (action.entry && action.release) {
208
- const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
209
- strapi
255
+ if (action.entry && action.release && action.type === "publish") {
256
+ const isEntryValid2 = await getDraftEntryValidStatus(
257
+ {
258
+ contentType: contentTypeUID,
259
+ documentId: action.entryDocumentId,
260
+ locale: action.locale
261
+ },
262
+ { strapi }
263
+ );
264
+ releasesAffected.add(action.release.id);
265
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
266
+ where: {
267
+ id: action.id
268
+ },
269
+ data: {
270
+ isEntryValid: isEntryValid2
271
+ }
210
272
  });
211
- if (populatedEntry) {
212
- const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
213
- strapi
214
- });
215
- releasesAffected.add(action.release.id);
216
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
217
- where: {
218
- id: action.id
219
- },
220
- data: {
221
- isEntryValid
222
- }
223
- });
224
- }
225
273
  }
226
274
  });
227
275
  }
@@ -278,9 +326,38 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
278
326
  }
279
327
  }
280
328
  }
329
+ const addEntryDocumentToReleaseActions = {
330
+ name: "content-releases::5.0.0-add-entry-document-id-to-release-actions",
331
+ async up(trx, db) {
332
+ const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
333
+ if (hasPolymorphicColumn) {
334
+ const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
335
+ "strapi_release_actions",
336
+ "entry_document_id"
337
+ );
338
+ if (!hasEntryDocumentIdColumn) {
339
+ await trx.schema.alterTable("strapi_release_actions", (table) => {
340
+ table.string("entry_document_id");
341
+ });
342
+ }
343
+ const releaseActions = await trx.select("*").from("strapi_release_actions");
344
+ utils.async.map(releaseActions, async (action) => {
345
+ const { target_type, target_id } = action;
346
+ const entry = await db.query(target_type).findOne({ where: { id: target_id } });
347
+ if (entry) {
348
+ await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
349
+ }
350
+ });
351
+ }
352
+ },
353
+ async down() {
354
+ throw new Error("not implemented");
355
+ }
356
+ };
281
357
  const register = async ({ strapi: strapi2 }) => {
282
358
  if (strapi2.ee.features.isEnabled("cms-content-releases")) {
283
359
  await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
360
+ strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
284
361
  strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
285
362
  strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
286
363
  }
@@ -290,6 +367,104 @@ const register = async ({ strapi: strapi2 }) => {
290
367
  graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
291
368
  }
292
369
  };
370
+ const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
371
+ const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
372
+ where: {
373
+ actions: {
374
+ contentType,
375
+ entryDocumentId: entry.documentId,
376
+ locale: entry.locale
377
+ }
378
+ }
379
+ });
380
+ const entryStatus = await isEntryValid(contentType, entry, { strapi });
381
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
382
+ where: {
383
+ contentType,
384
+ entryDocumentId: entry.documentId,
385
+ locale: entry.locale
386
+ },
387
+ data: {
388
+ isEntryValid: entryStatus
389
+ }
390
+ });
391
+ for (const release2 of releases) {
392
+ getService("release", { strapi }).updateReleaseStatus(release2.id);
393
+ }
394
+ };
395
+ const deleteActionsAndUpdateReleaseStatus = async (params) => {
396
+ const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
397
+ where: {
398
+ actions: params
399
+ }
400
+ });
401
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
402
+ where: params
403
+ });
404
+ for (const release2 of releases) {
405
+ getService("release", { strapi }).updateReleaseStatus(release2.id);
406
+ }
407
+ };
408
+ const deleteActionsOnDelete = async (ctx, next) => {
409
+ if (ctx.action !== "delete") {
410
+ return next();
411
+ }
412
+ if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
413
+ return next();
414
+ }
415
+ const contentType = ctx.contentType.uid;
416
+ const { documentId, locale } = ctx.params;
417
+ const result = await next();
418
+ if (!result) {
419
+ return result;
420
+ }
421
+ try {
422
+ deleteActionsAndUpdateReleaseStatus({
423
+ contentType,
424
+ entryDocumentId: documentId,
425
+ ...locale !== "*" && { locale }
426
+ });
427
+ } catch (error) {
428
+ strapi.log.error("Error while deleting release actions after delete", {
429
+ error
430
+ });
431
+ }
432
+ return result;
433
+ };
434
+ const updateActionsOnUpdate = async (ctx, next) => {
435
+ if (ctx.action !== "update") {
436
+ return next();
437
+ }
438
+ if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
439
+ return next();
440
+ }
441
+ const contentType = ctx.contentType.uid;
442
+ const result = await next();
443
+ if (!result) {
444
+ return result;
445
+ }
446
+ try {
447
+ updateActionsStatusAndUpdateReleaseStatus(contentType, result);
448
+ } catch (error) {
449
+ strapi.log.error("Error while updating release actions after update", {
450
+ error
451
+ });
452
+ }
453
+ return result;
454
+ };
455
+ const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
456
+ const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
457
+ where: {
458
+ actions: params
459
+ }
460
+ });
461
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
462
+ where: params
463
+ });
464
+ for (const release2 of releases) {
465
+ getService("release", { strapi }).updateReleaseStatus(release2.id);
466
+ }
467
+ };
293
468
  const bootstrap = async ({ strapi: strapi2 }) => {
294
469
  if (strapi2.ee.features.isEnabled("cms-content-releases")) {
295
470
  const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
@@ -297,115 +472,29 @@ const bootstrap = async ({ strapi: strapi2 }) => {
297
472
  );
298
473
  strapi2.db.lifecycles.subscribe({
299
474
  models: contentTypesWithDraftAndPublish,
300
- async afterDelete(event) {
301
- try {
302
- const { model, result } = event;
303
- if (model.kind === "collectionType" && model.options?.draftAndPublish) {
304
- const { id } = result;
305
- const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
306
- where: {
307
- actions: {
308
- target_type: model.uid,
309
- target_id: id
310
- }
311
- }
312
- });
313
- await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
314
- where: {
315
- target_type: model.uid,
316
- target_id: id
317
- }
318
- });
319
- for (const release2 of releases) {
320
- getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
321
- }
322
- }
323
- } catch (error) {
324
- strapi2.log.error("Error while deleting release actions after entry delete", { error });
325
- }
326
- },
327
475
  /**
328
- * deleteMany hook doesn't return the deleted entries ids
329
- * so we need to fetch them before deleting the entries to save the ids on our state
330
- */
331
- async beforeDeleteMany(event) {
332
- const { model, params } = event;
333
- if (model.kind === "collectionType" && model.options?.draftAndPublish) {
334
- const { where } = params;
335
- const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
336
- event.state.entriesToDelete = entriesToDelete;
337
- }
338
- },
339
- /**
340
- * We delete the release actions related to deleted entries
341
- * We make this only after deleteMany is succesfully executed to avoid errors
476
+ * deleteMany is still used outside documents service, for example when deleting a locale
342
477
  */
343
478
  async afterDeleteMany(event) {
344
479
  try {
345
- const { model, state } = event;
346
- const entriesToDelete = state.entriesToDelete;
347
- if (entriesToDelete) {
348
- const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
349
- where: {
350
- actions: {
351
- target_type: model.uid,
352
- target_id: {
353
- $in: entriesToDelete.map((entry) => entry.id)
354
- }
355
- }
356
- }
357
- });
358
- await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
359
- where: {
360
- target_type: model.uid,
361
- target_id: {
362
- $in: entriesToDelete.map((entry) => entry.id)
363
- }
364
- }
480
+ const model = strapi2.getModel(event.model.uid);
481
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
482
+ const { where } = event.params;
483
+ deleteReleasesActionsAndUpdateReleaseStatus({
484
+ contentType: model.uid,
485
+ locale: where.locale ?? null,
486
+ ...where.documentId && { entryDocumentId: where.documentId }
365
487
  });
366
- for (const release2 of releases) {
367
- getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
368
- }
369
488
  }
370
489
  } catch (error) {
371
490
  strapi2.log.error("Error while deleting release actions after entry deleteMany", {
372
491
  error
373
492
  });
374
493
  }
375
- },
376
- async afterUpdate(event) {
377
- try {
378
- const { model, result } = event;
379
- if (model.kind === "collectionType" && model.options?.draftAndPublish) {
380
- const isEntryValid = await getEntryValidStatus(model.uid, result, {
381
- strapi: strapi2
382
- });
383
- await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
384
- where: {
385
- target_type: model.uid,
386
- target_id: result.id
387
- },
388
- data: {
389
- isEntryValid
390
- }
391
- });
392
- const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
393
- where: {
394
- actions: {
395
- target_type: model.uid,
396
- target_id: result.id
397
- }
398
- }
399
- });
400
- for (const release2 of releases) {
401
- getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
402
- }
403
- }
404
- } catch (error) {
405
- strapi2.log.error("Error while updating release actions after entry update", { error });
406
- }
407
494
  }
408
495
  });
496
+ strapi2.documents.use(deleteActionsOnDelete);
497
+ strapi2.documents.use(updateActionsOnUpdate);
409
498
  getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
410
499
  strapi2.log.error(
411
500
  "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
@@ -497,15 +586,13 @@ const schema = {
497
586
  enum: ["publish", "unpublish"],
498
587
  required: true
499
588
  },
500
- entry: {
501
- type: "relation",
502
- relation: "morphToOne",
503
- configurable: false
504
- },
505
589
  contentType: {
506
590
  type: "string",
507
591
  required: true
508
592
  },
593
+ entryDocumentId: {
594
+ type: "string"
595
+ },
509
596
  locale: {
510
597
  type: "string"
511
598
  },
@@ -527,18 +614,6 @@ const contentTypes = {
527
614
  release: release$1,
528
615
  "release-action": releaseAction$1
529
616
  };
530
- const getGroupName = (queryValue) => {
531
- switch (queryValue) {
532
- case "contentType":
533
- return "contentType.displayName";
534
- case "action":
535
- return "type";
536
- case "locale":
537
- return ___default.default.getOr("No locale", "locale.name");
538
- default:
539
- return "contentType.displayName";
540
- }
541
- };
542
617
  const createReleaseService = ({ strapi: strapi2 }) => {
543
618
  const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
544
619
  strapi2.eventHub.emit(event, {
@@ -547,93 +622,32 @@ const createReleaseService = ({ strapi: strapi2 }) => {
547
622
  release: release2
548
623
  });
549
624
  };
550
- const publishSingleTypeAction = async (uid, actionType, entryId) => {
551
- const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
552
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
553
- const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
554
- const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
555
- try {
556
- if (actionType === "publish") {
557
- await entityManagerService.publish(entry, uid);
558
- } else {
559
- await entityManagerService.unpublish(entry, uid);
560
- }
561
- } catch (error) {
562
- if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
563
- ;
564
- else {
565
- throw error;
566
- }
567
- }
568
- };
569
- const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
570
- const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
571
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
572
- const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
573
- const entriesToPublish = await strapi2.entityService.findMany(uid, {
574
- filters: {
575
- id: {
576
- $in: entriesToPublishIds
577
- }
578
- },
579
- populate
580
- });
581
- const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
582
- filters: {
583
- id: {
584
- $in: entriestoUnpublishIds
585
- }
586
- },
587
- populate
588
- });
589
- if (entriesToPublish.length > 0) {
590
- await entityManagerService.publishMany(entriesToPublish, uid);
591
- }
592
- if (entriesToUnpublish.length > 0) {
593
- await entityManagerService.unpublishMany(entriesToUnpublish, uid);
594
- }
595
- };
596
625
  const getFormattedActions = async (releaseId) => {
597
626
  const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
598
627
  where: {
599
628
  release: {
600
629
  id: releaseId
601
630
  }
602
- },
603
- populate: {
604
- entry: {
605
- fields: ["id"]
606
- }
607
631
  }
608
632
  });
609
633
  if (actions.length === 0) {
610
634
  throw new utils.errors.ValidationError("No entries to publish");
611
635
  }
612
- const collectionTypeActions = {};
613
- const singleTypeActions = [];
636
+ const formattedActions = {};
614
637
  for (const action of actions) {
615
638
  const contentTypeUid = action.contentType;
616
- if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
617
- if (!collectionTypeActions[contentTypeUid]) {
618
- collectionTypeActions[contentTypeUid] = {
619
- entriesToPublishIds: [],
620
- entriesToUnpublishIds: []
621
- };
622
- }
623
- if (action.type === "publish") {
624
- collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
625
- } else {
626
- collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
627
- }
628
- } else {
629
- singleTypeActions.push({
630
- uid: contentTypeUid,
631
- action: action.type,
632
- id: action.entry.id
633
- });
639
+ if (!formattedActions[contentTypeUid]) {
640
+ formattedActions[contentTypeUid] = {
641
+ publish: [],
642
+ unpublish: []
643
+ };
634
644
  }
645
+ formattedActions[contentTypeUid][action.type].push({
646
+ documentId: action.entryDocumentId,
647
+ locale: action.locale
648
+ });
635
649
  }
636
- return { collectionTypeActions, singleTypeActions };
650
+ return formattedActions;
637
651
  };
638
652
  return {
639
653
  async create(releaseData, { user }) {
@@ -680,91 +694,10 @@ const createReleaseService = ({ strapi: strapi2 }) => {
680
694
  }
681
695
  });
682
696
  },
683
- async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
684
- let entries = entriesIds;
685
- if (!Array.isArray(entriesIds)) {
686
- entries = [entriesIds];
687
- }
688
- const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
689
- where: {
690
- actions: {
691
- target_type: contentTypeUid,
692
- target_id: {
693
- $in: entries
694
- }
695
- },
696
- releasedAt: {
697
- $null: true
698
- }
699
- },
700
- populate: {
701
- // Filter the action to get only the content type entry
702
- actions: {
703
- where: {
704
- target_type: contentTypeUid,
705
- target_id: {
706
- $in: entries
707
- }
708
- },
709
- populate: {
710
- entry: {
711
- select: ["id"]
712
- }
713
- }
714
- }
715
- }
716
- });
717
- return releases.map((release2) => {
718
- if (release2.actions?.length) {
719
- const actionsForEntry = release2.actions;
720
- delete release2.actions;
721
- return {
722
- ...release2,
723
- actions: actionsForEntry
724
- };
725
- }
726
- return release2;
727
- });
728
- },
729
- async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
730
- const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
731
- where: {
732
- releasedAt: {
733
- $null: true
734
- },
735
- actions: {
736
- target_type: contentTypeUid,
737
- target_id: entryId
738
- }
739
- }
740
- });
741
- const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
742
- where: {
743
- $or: [
744
- {
745
- id: {
746
- $notIn: releasesRelated.map((release2) => release2.id)
747
- }
748
- },
749
- {
750
- actions: null
751
- }
752
- ],
753
- releasedAt: {
754
- $null: true
755
- }
756
- }
757
- });
758
- return releases.map((release2) => {
759
- if (release2.actions?.length) {
760
- const [actionForEntry] = release2.actions;
761
- delete release2.actions;
762
- return {
763
- ...release2,
764
- action: actionForEntry
765
- };
766
- }
767
- return release2;
697
+ findMany(query) {
698
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
699
+ return strapi2.db.query(RELEASE_MODEL_UID).findMany({
700
+ ...dbQuery
768
701
  });
769
702
  },
770
703
  async update(id, releaseData, { user }) {
@@ -800,131 +733,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
800
733
  strapi2.telemetry.send("didUpdateContentRelease");
801
734
  return updatedRelease;
802
735
  },
803
- async createAction(releaseId, action) {
804
- const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
805
- strapi: strapi2
806
- });
807
- await Promise.all([
808
- validateEntryContentType(action.entry.contentType),
809
- validateUniqueEntry(releaseId, action)
810
- ]);
811
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
812
- if (!release2) {
813
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
814
- }
815
- if (release2.releasedAt) {
816
- throw new utils.errors.ValidationError("Release already published");
817
- }
818
- const { entry, type } = action;
819
- const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
820
- const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
821
- const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
822
- data: {
823
- type,
824
- contentType: entry.contentType,
825
- locale: entry.locale,
826
- isEntryValid,
827
- entry: {
828
- id: entry.id,
829
- __type: entry.contentType,
830
- __pivot: { field: "entry" }
831
- },
832
- release: releaseId
833
- },
834
- populate: { release: { select: ["id"] }, entry: { select: ["id"] } }
835
- });
836
- this.updateReleaseStatus(releaseId);
837
- return releaseAction2;
838
- },
839
- async findActions(releaseId, query) {
840
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
841
- where: { id: releaseId },
842
- select: ["id"]
843
- });
844
- if (!release2) {
845
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
846
- }
847
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
848
- return strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
849
- ...dbQuery,
850
- populate: {
851
- entry: {
852
- populate: "*"
853
- }
854
- },
855
- where: {
856
- release: releaseId
857
- }
858
- });
859
- },
860
- async countActions(query) {
861
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
862
- return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
863
- },
864
- async groupActions(actions, groupBy) {
865
- const contentTypeUids = actions.reduce((acc, action) => {
866
- if (!acc.includes(action.contentType)) {
867
- acc.push(action.contentType);
868
- }
869
- return acc;
870
- }, []);
871
- const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(contentTypeUids);
872
- const allLocalesDictionary = await this.getLocalesDataForActions();
873
- const formattedData = actions.map((action) => {
874
- const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
875
- return {
876
- ...action,
877
- locale: action.locale ? allLocalesDictionary[action.locale] : null,
878
- contentType: {
879
- displayName,
880
- mainFieldValue: action.entry[mainField],
881
- uid: action.contentType
882
- }
883
- };
884
- });
885
- const groupName = getGroupName(groupBy);
886
- return ___default.default.groupBy(groupName)(formattedData);
887
- },
888
- async getLocalesDataForActions() {
889
- if (!strapi2.plugin("i18n")) {
890
- return {};
891
- }
892
- const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
893
- return allLocales.reduce((acc, locale) => {
894
- acc[locale.code] = { name: locale.name, code: locale.code };
895
- return acc;
896
- }, {});
897
- },
898
- async getContentTypesDataForActions(contentTypesUids) {
899
- const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
900
- const contentTypesData = {};
901
- for (const contentTypeUid of contentTypesUids) {
902
- const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
903
- uid: contentTypeUid
904
- });
905
- contentTypesData[contentTypeUid] = {
906
- mainField: contentTypeConfig.settings.mainField,
907
- displayName: strapi2.getModel(contentTypeUid).info.displayName
908
- };
909
- }
910
- return contentTypesData;
911
- },
912
- getContentTypeModelsFromActions(actions) {
913
- const contentTypeUids = actions.reduce((acc, action) => {
914
- if (!acc.includes(action.contentType)) {
915
- acc.push(action.contentType);
916
- }
917
- return acc;
918
- }, []);
919
- const contentTypeModelsMap = contentTypeUids.reduce(
920
- (acc, contentTypeUid) => {
921
- acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
922
- return acc;
923
- },
924
- {}
925
- );
926
- return contentTypeModelsMap;
927
- },
928
736
  async getAllComponents() {
929
737
  const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
930
738
  const components = await contentManagerComponentsService.findAllComponents();
@@ -990,20 +798,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
990
798
  }
991
799
  try {
992
800
  strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
993
- const { collectionTypeActions, singleTypeActions } = await getFormattedActions(releaseId);
994
- await strapi2.db.transaction(async () => {
995
- for (const { uid, action, id } of singleTypeActions) {
996
- await publishSingleTypeAction(uid, action, id);
997
- }
998
- for (const contentTypeUid of Object.keys(collectionTypeActions)) {
999
- const uid = contentTypeUid;
1000
- await publishCollectionTypeAction(
1001
- uid,
1002
- collectionTypeActions[uid].entriesToPublishIds,
1003
- collectionTypeActions[uid].entriesToUnpublishIds
1004
- );
1005
- }
1006
- });
801
+ const formattedActions = await getFormattedActions(releaseId);
802
+ await strapi2.db.transaction(
803
+ async () => Promise.all(
804
+ Object.keys(formattedActions).map(async (contentTypeUid) => {
805
+ const contentType = contentTypeUid;
806
+ const { publish, unpublish } = formattedActions[contentType];
807
+ return Promise.all([
808
+ ...publish.map((params) => strapi2.documents(contentType).publish(params)),
809
+ ...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
810
+ ]);
811
+ })
812
+ )
813
+ );
1007
814
  const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
1008
815
  where: {
1009
816
  id: releaseId
@@ -1033,13 +840,216 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1033
840
  };
1034
841
  }
1035
842
  });
1036
- if (error instanceof Error) {
1037
- throw error;
843
+ if (error instanceof Error) {
844
+ throw error;
845
+ }
846
+ return release2;
847
+ },
848
+ async updateReleaseStatus(releaseId) {
849
+ const releaseActionService = getService("release-action", { strapi: strapi2 });
850
+ const [totalActions, invalidActions] = await Promise.all([
851
+ releaseActionService.countActions({
852
+ filters: {
853
+ release: releaseId
854
+ }
855
+ }),
856
+ releaseActionService.countActions({
857
+ filters: {
858
+ release: releaseId,
859
+ isEntryValid: false
860
+ }
861
+ })
862
+ ]);
863
+ if (totalActions > 0) {
864
+ if (invalidActions > 0) {
865
+ return strapi2.db.query(RELEASE_MODEL_UID).update({
866
+ where: {
867
+ id: releaseId
868
+ },
869
+ data: {
870
+ status: "blocked"
871
+ }
872
+ });
873
+ }
874
+ return strapi2.db.query(RELEASE_MODEL_UID).update({
875
+ where: {
876
+ id: releaseId
877
+ },
878
+ data: {
879
+ status: "ready"
880
+ }
881
+ });
882
+ }
883
+ return strapi2.db.query(RELEASE_MODEL_UID).update({
884
+ where: {
885
+ id: releaseId
886
+ },
887
+ data: {
888
+ status: "empty"
889
+ }
890
+ });
891
+ }
892
+ };
893
+ };
894
+ const getGroupName = (queryValue) => {
895
+ switch (queryValue) {
896
+ case "contentType":
897
+ return "contentType.displayName";
898
+ case "type":
899
+ return "type";
900
+ case "locale":
901
+ return ___default.default.getOr("No locale", "locale.name");
902
+ default:
903
+ return "contentType.displayName";
904
+ }
905
+ };
906
+ const createReleaseActionService = ({ strapi: strapi2 }) => {
907
+ const getLocalesDataForActions = async () => {
908
+ if (!strapi2.plugin("i18n")) {
909
+ return {};
910
+ }
911
+ const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
912
+ return allLocales.reduce((acc, locale) => {
913
+ acc[locale.code] = { name: locale.name, code: locale.code };
914
+ return acc;
915
+ }, {});
916
+ };
917
+ const getContentTypesDataForActions = async (contentTypesUids) => {
918
+ const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
919
+ const contentTypesData = {};
920
+ for (const contentTypeUid of contentTypesUids) {
921
+ const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
922
+ uid: contentTypeUid
923
+ });
924
+ contentTypesData[contentTypeUid] = {
925
+ mainField: contentTypeConfig.settings.mainField,
926
+ displayName: strapi2.getModel(contentTypeUid).info.displayName
927
+ };
928
+ }
929
+ return contentTypesData;
930
+ };
931
+ return {
932
+ async create(releaseId, action) {
933
+ const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
934
+ strapi: strapi2
935
+ });
936
+ await Promise.all([
937
+ validateEntryData(action.contentType, action.entryDocumentId),
938
+ validateUniqueEntry(releaseId, action)
939
+ ]);
940
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
941
+ if (!release2) {
942
+ throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
943
+ }
944
+ if (release2.releasedAt) {
945
+ throw new utils.errors.ValidationError("Release already published");
946
+ }
947
+ const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
948
+ {
949
+ contentType: action.contentType,
950
+ documentId: action.entryDocumentId,
951
+ locale: action.locale
952
+ },
953
+ {
954
+ strapi: strapi2
955
+ }
956
+ ) : true;
957
+ const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
958
+ data: {
959
+ ...action,
960
+ release: release2.id,
961
+ isEntryValid: actionStatus
962
+ },
963
+ populate: { release: { select: ["id"] } }
964
+ });
965
+ getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
966
+ return releaseAction2;
967
+ },
968
+ async findPage(releaseId, query) {
969
+ const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
970
+ where: { id: releaseId },
971
+ select: ["id"]
972
+ });
973
+ if (!release2) {
974
+ throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1038
975
  }
1039
- return release2;
976
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
977
+ const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
978
+ ...dbQuery,
979
+ where: {
980
+ release: releaseId
981
+ }
982
+ });
983
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
984
+ const actionsWithEntry = await utils.async.map(actions, async (action) => {
985
+ const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
986
+ const entry = await getEntry(
987
+ {
988
+ contentType: action.contentType,
989
+ documentId: action.entryDocumentId,
990
+ locale: action.locale,
991
+ populate,
992
+ status: action.type === "publish" ? "draft" : "published"
993
+ },
994
+ { strapi: strapi2 }
995
+ );
996
+ return {
997
+ ...action,
998
+ entry,
999
+ status: entry ? await getEntryStatus(action.contentType, entry) : null
1000
+ };
1001
+ });
1002
+ return {
1003
+ results: actionsWithEntry,
1004
+ pagination
1005
+ };
1040
1006
  },
1041
- async updateAction(actionId, releaseId, update) {
1042
- const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
1007
+ async groupActions(actions, groupBy) {
1008
+ const contentTypeUids = actions.reduce((acc, action) => {
1009
+ if (!acc.includes(action.contentType)) {
1010
+ acc.push(action.contentType);
1011
+ }
1012
+ return acc;
1013
+ }, []);
1014
+ const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
1015
+ const allLocalesDictionary = await getLocalesDataForActions();
1016
+ const formattedData = actions.map((action) => {
1017
+ const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
1018
+ return {
1019
+ ...action,
1020
+ locale: action.locale ? allLocalesDictionary[action.locale] : null,
1021
+ contentType: {
1022
+ displayName,
1023
+ mainFieldValue: action.entry[mainField],
1024
+ uid: action.contentType
1025
+ }
1026
+ };
1027
+ });
1028
+ const groupName = getGroupName(groupBy);
1029
+ return ___default.default.groupBy(groupName)(formattedData);
1030
+ },
1031
+ getContentTypeModelsFromActions(actions) {
1032
+ const contentTypeUids = actions.reduce((acc, action) => {
1033
+ if (!acc.includes(action.contentType)) {
1034
+ acc.push(action.contentType);
1035
+ }
1036
+ return acc;
1037
+ }, []);
1038
+ const contentTypeModelsMap = contentTypeUids.reduce(
1039
+ (acc, contentTypeUid) => {
1040
+ acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
1041
+ return acc;
1042
+ },
1043
+ {}
1044
+ );
1045
+ return contentTypeModelsMap;
1046
+ },
1047
+ async countActions(query) {
1048
+ const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
1049
+ return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
1050
+ },
1051
+ async update(actionId, releaseId, update) {
1052
+ const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
1043
1053
  where: {
1044
1054
  id: actionId,
1045
1055
  release: {
@@ -1048,17 +1058,42 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1048
1058
  $null: true
1049
1059
  }
1050
1060
  }
1051
- },
1052
- data: update
1061
+ }
1053
1062
  });
1054
- if (!updatedAction) {
1063
+ if (!action) {
1055
1064
  throw new utils.errors.NotFoundError(
1056
1065
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1057
1066
  );
1058
1067
  }
1068
+ const actionStatus = update.type === "publish" ? getDraftEntryValidStatus(
1069
+ {
1070
+ contentType: action.contentType,
1071
+ documentId: action.entryDocumentId,
1072
+ locale: action.locale
1073
+ },
1074
+ {
1075
+ strapi: strapi2
1076
+ }
1077
+ ) : true;
1078
+ const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
1079
+ where: {
1080
+ id: actionId,
1081
+ release: {
1082
+ id: releaseId,
1083
+ releasedAt: {
1084
+ $null: true
1085
+ }
1086
+ }
1087
+ },
1088
+ data: {
1089
+ ...update,
1090
+ isEntryValid: actionStatus
1091
+ }
1092
+ });
1093
+ getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1059
1094
  return updatedAction;
1060
1095
  },
1061
- async deleteAction(actionId, releaseId) {
1096
+ async delete(actionId, releaseId) {
1062
1097
  const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
1063
1098
  where: {
1064
1099
  id: actionId,
@@ -1075,51 +1110,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
1075
1110
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1076
1111
  );
1077
1112
  }
1078
- this.updateReleaseStatus(releaseId);
1113
+ getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1079
1114
  return deletedAction;
1080
- },
1081
- async updateReleaseStatus(releaseId) {
1082
- const [totalActions, invalidActions] = await Promise.all([
1083
- this.countActions({
1084
- filters: {
1085
- release: releaseId
1086
- }
1087
- }),
1088
- this.countActions({
1089
- filters: {
1090
- release: releaseId,
1091
- isEntryValid: false
1092
- }
1093
- })
1094
- ]);
1095
- if (totalActions > 0) {
1096
- if (invalidActions > 0) {
1097
- return strapi2.db.query(RELEASE_MODEL_UID).update({
1098
- where: {
1099
- id: releaseId
1100
- },
1101
- data: {
1102
- status: "blocked"
1103
- }
1104
- });
1105
- }
1106
- return strapi2.db.query(RELEASE_MODEL_UID).update({
1107
- where: {
1108
- id: releaseId
1109
- },
1110
- data: {
1111
- status: "ready"
1112
- }
1113
- });
1114
- }
1115
- return strapi2.db.query(RELEASE_MODEL_UID).update({
1116
- where: {
1117
- id: releaseId
1118
- },
1119
- data: {
1120
- status: "empty"
1121
- }
1122
- });
1123
1115
  }
1124
1116
  };
1125
1117
  };
@@ -1135,30 +1127,35 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1135
1127
  where: {
1136
1128
  id: releaseId
1137
1129
  },
1138
- populate: { actions: { populate: { entry: { select: ["id"] } } } }
1130
+ populate: {
1131
+ actions: true
1132
+ }
1139
1133
  });
1140
1134
  if (!release2) {
1141
1135
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1142
1136
  }
1143
1137
  const isEntryInRelease = release2.actions.some(
1144
- (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1138
+ (action) => Number(action.entryDocumentId) === Number(releaseActionArgs.entryDocumentId) && action.contentType === releaseActionArgs.contentType && action.locale === releaseActionArgs.locale
1145
1139
  );
1146
1140
  if (isEntryInRelease) {
1147
1141
  throw new AlreadyOnReleaseError(
1148
- `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1142
+ `Entry with documentId ${releaseActionArgs.entryDocumentId} ${releaseActionArgs.locale ? `(${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
1149
1143
  );
1150
1144
  }
1151
1145
  },
1152
- validateEntryContentType(contentTypeUid) {
1146
+ validateEntryData(contentTypeUid, entryDocumentId) {
1153
1147
  const contentType = strapi2.contentType(contentTypeUid);
1154
1148
  if (!contentType) {
1155
1149
  throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
1156
1150
  }
1157
- if (!contentType.options?.draftAndPublish) {
1151
+ if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
1158
1152
  throw new utils.errors.ValidationError(
1159
1153
  `Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
1160
1154
  );
1161
1155
  }
1156
+ if (contentType.kind === "collectionType" && !entryDocumentId) {
1157
+ throw new utils.errors.ValidationError("Document id is required for collection type");
1158
+ }
1162
1159
  },
1163
1160
  async validatePendingReleasesLimit() {
1164
1161
  const featureCfg = strapi2.ee.features.get("cms-content-releases");
@@ -1247,10 +1244,33 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1247
1244
  }
1248
1245
  };
1249
1246
  };
1247
+ const DEFAULT_SETTINGS = {
1248
+ defaultTimezone: null
1249
+ };
1250
+ const createSettingsService = ({ strapi: strapi2 }) => {
1251
+ const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
1252
+ return {
1253
+ async update({ settings: settings2 }) {
1254
+ const store = await getStore();
1255
+ store.set({ key: "settings", value: settings2 });
1256
+ return settings2;
1257
+ },
1258
+ async find() {
1259
+ const store = await getStore();
1260
+ const settings2 = await store.get({ key: "settings" });
1261
+ return {
1262
+ ...DEFAULT_SETTINGS,
1263
+ ...settings2 || {}
1264
+ };
1265
+ }
1266
+ };
1267
+ };
1250
1268
  const services = {
1251
1269
  release: createReleaseService,
1270
+ "release-action": createReleaseActionService,
1252
1271
  "release-validation": createReleaseValidationService,
1253
- scheduling: createSchedulingService
1272
+ scheduling: createSchedulingService,
1273
+ settings: createSettingsService
1254
1274
  };
1255
1275
  const RELEASE_SCHEMA = yup__namespace.object().shape({
1256
1276
  name: yup__namespace.string().trim().required(),
@@ -1272,53 +1292,118 @@ const RELEASE_SCHEMA = yup__namespace.object().shape({
1272
1292
  otherwise: yup__namespace.string().nullable()
1273
1293
  })
1274
1294
  }).required().noUnknown();
1295
+ const SETTINGS_SCHEMA = yup__namespace.object().shape({
1296
+ defaultTimezone: yup__namespace.string().nullable().default(null)
1297
+ }).required().noUnknown();
1298
+ const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
1299
+ contentType: utils.yup.string().required(),
1300
+ entryDocumentId: utils.yup.string().nullable(),
1301
+ hasEntryAttached: utils.yup.string().nullable(),
1302
+ locale: utils.yup.string().nullable()
1303
+ }).required().noUnknown();
1275
1304
  const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
1305
+ const validatefindByDocumentAttachedParams = utils.validateYupSchema(
1306
+ FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
1307
+ );
1276
1308
  const releaseController = {
1277
- async findMany(ctx) {
1309
+ /**
1310
+ * Find releases based on documents attached or not to the release.
1311
+ * If `hasEntryAttached` is true, it will return all releases that have the entry attached.
1312
+ * If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
1313
+ */
1314
+ async findByDocumentAttached(ctx) {
1278
1315
  const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1279
1316
  ability: ctx.state.userAbility,
1280
1317
  model: RELEASE_MODEL_UID
1281
1318
  });
1282
1319
  await permissionsManager.validateQuery(ctx.query);
1283
1320
  const releaseService = getService("release", { strapi });
1284
- const isFindManyForContentTypeEntry = Boolean(ctx.query?.contentTypeUid && ctx.query?.entryId);
1285
- if (isFindManyForContentTypeEntry) {
1286
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1287
- const contentTypeUid = query.contentTypeUid;
1288
- const entryId = query.entryId;
1289
- const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
1290
- const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
1291
- ctx.body = { data };
1321
+ const query = await permissionsManager.sanitizeQuery(ctx.query);
1322
+ await validatefindByDocumentAttachedParams(query);
1323
+ const { contentType, entryDocumentId, hasEntryAttached, locale } = query;
1324
+ const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
1325
+ if (isEntryAttached) {
1326
+ const releases = await releaseService.findMany({
1327
+ where: {
1328
+ releasedAt: null,
1329
+ actions: {
1330
+ contentType,
1331
+ entryDocumentId: entryDocumentId ?? null,
1332
+ locale: locale ?? null
1333
+ }
1334
+ },
1335
+ populate: {
1336
+ actions: {
1337
+ fields: ["type"]
1338
+ }
1339
+ }
1340
+ });
1341
+ ctx.body = { data: releases };
1292
1342
  } else {
1293
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1294
- const { results, pagination } = await releaseService.findPage(query);
1295
- const data = results.map((release2) => {
1296
- const { actions, ...releaseData } = release2;
1297
- return {
1298
- ...releaseData,
1343
+ const relatedReleases = await releaseService.findMany({
1344
+ where: {
1345
+ releasedAt: null,
1299
1346
  actions: {
1300
- meta: {
1301
- count: actions.count
1302
- }
1347
+ contentType,
1348
+ entryDocumentId: entryDocumentId ?? null,
1349
+ locale: locale ?? null
1303
1350
  }
1304
- };
1351
+ }
1305
1352
  });
1306
- const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1353
+ const releases = await releaseService.findMany({
1307
1354
  where: {
1355
+ $or: [
1356
+ {
1357
+ id: {
1358
+ $notIn: relatedReleases.map((release2) => release2.id)
1359
+ }
1360
+ },
1361
+ {
1362
+ actions: null
1363
+ }
1364
+ ],
1308
1365
  releasedAt: null
1309
1366
  }
1310
1367
  });
1311
- ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1368
+ ctx.body = { data: releases };
1312
1369
  }
1313
1370
  },
1371
+ async findPage(ctx) {
1372
+ const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1373
+ ability: ctx.state.userAbility,
1374
+ model: RELEASE_MODEL_UID
1375
+ });
1376
+ await permissionsManager.validateQuery(ctx.query);
1377
+ const releaseService = getService("release", { strapi });
1378
+ const query = await permissionsManager.sanitizeQuery(ctx.query);
1379
+ const { results, pagination } = await releaseService.findPage(query);
1380
+ const data = results.map((release2) => {
1381
+ const { actions, ...releaseData } = release2;
1382
+ return {
1383
+ ...releaseData,
1384
+ actions: {
1385
+ meta: {
1386
+ count: actions.count
1387
+ }
1388
+ }
1389
+ };
1390
+ });
1391
+ const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1392
+ where: {
1393
+ releasedAt: null
1394
+ }
1395
+ });
1396
+ ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1397
+ },
1314
1398
  async findOne(ctx) {
1315
1399
  const id = ctx.params.id;
1316
1400
  const releaseService = getService("release", { strapi });
1401
+ const releaseActionService = getService("release-action", { strapi });
1317
1402
  const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
1318
1403
  if (!release2) {
1319
1404
  throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
1320
1405
  }
1321
- const count = await releaseService.countActions({
1406
+ const count = await releaseActionService.countActions({
1322
1407
  filters: {
1323
1408
  release: id
1324
1409
  }
@@ -1337,35 +1422,43 @@ const releaseController = {
1337
1422
  };
1338
1423
  ctx.body = { data };
1339
1424
  },
1340
- async mapEntriesToReleases(ctx) {
1341
- const { contentTypeUid, entriesIds } = ctx.query;
1342
- if (!contentTypeUid || !entriesIds) {
1343
- throw new utils.errors.ValidationError("Missing required query parameters");
1344
- }
1345
- const releaseService = getService("release", { strapi });
1346
- const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1347
- contentTypeUid,
1348
- entriesIds
1349
- );
1350
- const mappedEntriesInReleases = releasesWithActions.reduce(
1351
- // TODO: Fix for v5 removed mappedEntriedToRelease
1352
- (acc, release2) => {
1353
- release2.actions.forEach((action) => {
1354
- if (!acc[action.entry.id]) {
1355
- acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
1356
- } else {
1357
- acc[action.entry.id].push({ id: release2.id, name: release2.name });
1358
- }
1359
- });
1360
- return acc;
1361
- },
1362
- // TODO: Fix for v5 removed mappedEntriedToRelease
1363
- {}
1364
- );
1365
- ctx.body = {
1366
- data: mappedEntriesInReleases
1367
- };
1368
- },
1425
+ /* @TODO: Migrate to new api
1426
+ async mapEntriesToReleases(ctx: Koa.Context) {
1427
+ const { contentTypeUid, entriesIds } = ctx.query;
1428
+
1429
+ if (!contentTypeUid || !entriesIds) {
1430
+ throw new errors.ValidationError('Missing required query parameters');
1431
+ }
1432
+
1433
+ const releaseService = getService('release', { strapi });
1434
+
1435
+ const releasesWithActions = await releaseService.findMany(
1436
+ contentTypeUid,
1437
+ entriesIds
1438
+ );
1439
+
1440
+ const mappedEntriesInReleases = releasesWithActions.reduce(
1441
+ // TODO: Fix for v5 removed mappedEntriedToRelease
1442
+ (acc: MapEntriesToReleases.Response['data'], release: Release) => {
1443
+ release.actions.forEach((action) => {
1444
+ if (!acc[action.entry.id]) {
1445
+ acc[action.entry.id] = [{ id: release.id, name: release.name }];
1446
+ } else {
1447
+ acc[action.entry.id].push({ id: release.id, name: release.name });
1448
+ }
1449
+ });
1450
+
1451
+ return acc;
1452
+ },
1453
+ // TODO: Fix for v5 removed mappedEntriedToRelease
1454
+ {} as MapEntriesToReleases.Response['data']
1455
+ );
1456
+
1457
+ ctx.body = {
1458
+ data: mappedEntriesInReleases,
1459
+ };
1460
+ },
1461
+ */
1369
1462
  async create(ctx) {
1370
1463
  const user = ctx.state.user;
1371
1464
  const releaseArgs = ctx.request.body;
@@ -1404,18 +1497,18 @@ const releaseController = {
1404
1497
  };
1405
1498
  },
1406
1499
  async publish(ctx) {
1407
- const user = ctx.state.user;
1408
1500
  const id = ctx.params.id;
1409
1501
  const releaseService = getService("release", { strapi });
1410
- const release2 = await releaseService.publish(id, { user });
1502
+ const releaseActionService = getService("release-action", { strapi });
1503
+ const release2 = await releaseService.publish(id);
1411
1504
  const [countPublishActions, countUnpublishActions] = await Promise.all([
1412
- releaseService.countActions({
1505
+ releaseActionService.countActions({
1413
1506
  filters: {
1414
1507
  release: id,
1415
1508
  type: "publish"
1416
1509
  }
1417
1510
  }),
1418
- releaseService.countActions({
1511
+ releaseActionService.countActions({
1419
1512
  filters: {
1420
1513
  release: id,
1421
1514
  type: "unpublish"
@@ -1433,42 +1526,47 @@ const releaseController = {
1433
1526
  }
1434
1527
  };
1435
1528
  const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
1436
- entry: utils.yup.object().shape({
1437
- id: utils.yup.strapiID().required(),
1438
- contentType: utils.yup.string().required()
1439
- }).required(),
1529
+ contentType: utils.yup.string().required(),
1530
+ entryDocumentId: utils.yup.strapiID(),
1531
+ locale: utils.yup.string(),
1440
1532
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1441
1533
  });
1442
1534
  const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
1443
1535
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1444
1536
  });
1537
+ const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
1538
+ groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
1539
+ });
1445
1540
  const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
1446
1541
  const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
1542
+ const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
1447
1543
  const releaseActionController = {
1448
1544
  async create(ctx) {
1449
1545
  const releaseId = ctx.params.releaseId;
1450
1546
  const releaseActionArgs = ctx.request.body;
1451
1547
  await validateReleaseAction(releaseActionArgs);
1452
- const releaseService = getService("release", { strapi });
1453
- const releaseAction2 = await releaseService.createAction(releaseId, releaseActionArgs);
1548
+ const releaseActionService = getService("release-action", { strapi });
1549
+ const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
1454
1550
  ctx.created({
1455
1551
  data: releaseAction2
1456
1552
  });
1457
1553
  },
1458
- async createMany(ctx) {
1459
- const releaseId = ctx.params.releaseId;
1460
- const releaseActionsArgs = ctx.request.body;
1554
+ /*
1555
+ async createMany(ctx: Koa.Context) {
1556
+ const releaseId: CreateManyReleaseActions.Request['params']['releaseId'] = ctx.params.releaseId;
1557
+ const releaseActionsArgs = ctx.request.body as CreateManyReleaseActions.Request['body'];
1461
1558
  await Promise.all(
1462
1559
  releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1463
1560
  );
1464
- const releaseService = getService("release", { strapi });
1561
+ const releaseActionService = getService('release-action', { strapi });
1465
1562
  const releaseActions = await strapi.db.transaction(async () => {
1466
- const releaseActions2 = await Promise.all(
1563
+ const releaseActions = await Promise.all(
1467
1564
  releaseActionsArgs.map(async (releaseActionArgs) => {
1468
1565
  try {
1469
- const action = await releaseService.createAction(releaseId, releaseActionArgs);
1566
+ const action = await releaseActionService.create(releaseId, releaseActionArgs);
1470
1567
  return action;
1471
1568
  } catch (error) {
1569
+ // If the entry is already in the release, we don't want to throw an error, so we catch and ignore it
1472
1570
  if (error instanceof AlreadyOnReleaseError) {
1473
1571
  return null;
1474
1572
  }
@@ -1476,27 +1574,35 @@ const releaseActionController = {
1476
1574
  }
1477
1575
  })
1478
1576
  );
1479
- return releaseActions2;
1577
+ return releaseActions;
1480
1578
  });
1481
1579
  const newReleaseActions = releaseActions.filter((action) => action !== null);
1482
1580
  ctx.created({
1483
1581
  data: newReleaseActions,
1484
1582
  meta: {
1485
1583
  entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1486
- totalEntries: releaseActions.length
1487
- }
1584
+ totalEntries: releaseActions.length,
1585
+ },
1488
1586
  });
1489
1587
  },
1588
+ */
1490
1589
  async findMany(ctx) {
1491
1590
  const releaseId = ctx.params.releaseId;
1492
1591
  const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1493
1592
  ability: ctx.state.userAbility,
1494
1593
  model: RELEASE_ACTION_MODEL_UID
1495
1594
  });
1595
+ await validateFindManyActionsParams(ctx.query);
1596
+ if (ctx.query.groupBy) {
1597
+ if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
1598
+ ctx.badRequest("Invalid groupBy parameter");
1599
+ }
1600
+ }
1601
+ ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
1602
+ delete ctx.query.groupBy;
1496
1603
  const query = await permissionsManager.sanitizeQuery(ctx.query);
1497
- const releaseService = getService("release", { strapi });
1498
- const { results, pagination } = await releaseService.findActions(releaseId, {
1499
- sort: query.groupBy === "action" ? "type" : query.groupBy,
1604
+ const releaseActionService = getService("release-action", { strapi });
1605
+ const { results, pagination } = await releaseActionService.findPage(releaseId, {
1500
1606
  ...query
1501
1607
  });
1502
1608
  const contentTypeOutputSanitizers = results.reduce((acc, action) => {
@@ -1512,10 +1618,11 @@ const releaseActionController = {
1512
1618
  }, {});
1513
1619
  const sanitizedResults = await utils.async.map(results, async (action) => ({
1514
1620
  ...action,
1515
- entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
1621
+ entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
1516
1622
  }));
1517
- const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);
1518
- const contentTypes2 = releaseService.getContentTypeModelsFromActions(results);
1623
+ const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
1624
+ const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
1625
+ const releaseService = getService("release", { strapi });
1519
1626
  const components = await releaseService.getAllComponents();
1520
1627
  ctx.body = {
1521
1628
  data: groupedData,
@@ -1531,8 +1638,8 @@ const releaseActionController = {
1531
1638
  const releaseId = ctx.params.releaseId;
1532
1639
  const releaseActionUpdateArgs = ctx.request.body;
1533
1640
  await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
1534
- const releaseService = getService("release", { strapi });
1535
- const updatedAction = await releaseService.updateAction(
1641
+ const releaseActionService = getService("release-action", { strapi });
1642
+ const updatedAction = await releaseActionService.update(
1536
1643
  actionId,
1537
1644
  releaseId,
1538
1645
  releaseActionUpdateArgs
@@ -1544,21 +1651,58 @@ const releaseActionController = {
1544
1651
  async delete(ctx) {
1545
1652
  const actionId = ctx.params.actionId;
1546
1653
  const releaseId = ctx.params.releaseId;
1547
- const releaseService = getService("release", { strapi });
1548
- const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
1654
+ const releaseActionService = getService("release-action", { strapi });
1655
+ const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
1549
1656
  ctx.body = {
1550
1657
  data: deletedReleaseAction
1551
1658
  };
1552
1659
  }
1553
1660
  };
1554
- const controllers = { release: releaseController, "release-action": releaseActionController };
1661
+ const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
1662
+ const settingsController = {
1663
+ async find(ctx) {
1664
+ const settingsService = getService("settings", { strapi });
1665
+ const settings2 = await settingsService.find();
1666
+ ctx.body = { data: settings2 };
1667
+ },
1668
+ async update(ctx) {
1669
+ const settingsBody = ctx.request.body;
1670
+ const settings2 = await validateSettings(settingsBody);
1671
+ const settingsService = getService("settings", { strapi });
1672
+ const updatedSettings = await settingsService.update({ settings: settings2 });
1673
+ ctx.body = { data: updatedSettings };
1674
+ }
1675
+ };
1676
+ const controllers = {
1677
+ release: releaseController,
1678
+ "release-action": releaseActionController,
1679
+ settings: settingsController
1680
+ };
1555
1681
  const release = {
1556
1682
  type: "admin",
1557
1683
  routes: [
1684
+ /*
1685
+ {
1686
+ method: 'GET',
1687
+ path: '/mapEntriesToReleases',
1688
+ handler: 'release.mapEntriesToReleases',
1689
+ config: {
1690
+ policies: [
1691
+ 'admin::isAuthenticatedAdmin',
1692
+ {
1693
+ name: 'admin::hasPermissions',
1694
+ config: {
1695
+ actions: ['plugin::content-releases.read'],
1696
+ },
1697
+ },
1698
+ ],
1699
+ },
1700
+ },
1701
+ */
1558
1702
  {
1559
1703
  method: "GET",
1560
- path: "/mapEntriesToReleases",
1561
- handler: "release.mapEntriesToReleases",
1704
+ path: "/getByDocumentAttached",
1705
+ handler: "release.findByDocumentAttached",
1562
1706
  config: {
1563
1707
  policies: [
1564
1708
  "admin::isAuthenticatedAdmin",
@@ -1590,7 +1734,7 @@ const release = {
1590
1734
  {
1591
1735
  method: "GET",
1592
1736
  path: "/",
1593
- handler: "release.findMany",
1737
+ handler: "release.findPage",
1594
1738
  config: {
1595
1739
  policies: [
1596
1740
  "admin::isAuthenticatedAdmin",
@@ -1688,22 +1832,24 @@ const releaseAction = {
1688
1832
  ]
1689
1833
  }
1690
1834
  },
1835
+ /*
1691
1836
  {
1692
- method: "POST",
1693
- path: "/:releaseId/actions/bulk",
1694
- handler: "release-action.createMany",
1837
+ method: 'POST',
1838
+ path: '/:releaseId/actions/bulk',
1839
+ handler: 'release-action.createMany',
1695
1840
  config: {
1696
1841
  policies: [
1697
- "admin::isAuthenticatedAdmin",
1842
+ 'admin::isAuthenticatedAdmin',
1698
1843
  {
1699
- name: "admin::hasPermissions",
1844
+ name: 'admin::hasPermissions',
1700
1845
  config: {
1701
- actions: ["plugin::content-releases.create-action"]
1702
- }
1703
- }
1704
- ]
1705
- }
1846
+ actions: ['plugin::content-releases.create-action'],
1847
+ },
1848
+ },
1849
+ ],
1850
+ },
1706
1851
  },
1852
+ */
1707
1853
  {
1708
1854
  method: "GET",
1709
1855
  path: "/:releaseId/actions",
@@ -1754,7 +1900,45 @@ const releaseAction = {
1754
1900
  }
1755
1901
  ]
1756
1902
  };
1903
+ const settings = {
1904
+ type: "admin",
1905
+ routes: [
1906
+ {
1907
+ method: "GET",
1908
+ path: "/settings",
1909
+ handler: "settings.find",
1910
+ config: {
1911
+ policies: [
1912
+ "admin::isAuthenticatedAdmin",
1913
+ {
1914
+ name: "admin::hasPermissions",
1915
+ config: {
1916
+ actions: ["plugin::content-releases.settings.read"]
1917
+ }
1918
+ }
1919
+ ]
1920
+ }
1921
+ },
1922
+ {
1923
+ method: "PUT",
1924
+ path: "/settings",
1925
+ handler: "settings.update",
1926
+ config: {
1927
+ policies: [
1928
+ "admin::isAuthenticatedAdmin",
1929
+ {
1930
+ name: "admin::hasPermissions",
1931
+ config: {
1932
+ actions: ["plugin::content-releases.settings.update"]
1933
+ }
1934
+ }
1935
+ ]
1936
+ }
1937
+ }
1938
+ ]
1939
+ };
1757
1940
  const routes = {
1941
+ settings,
1758
1942
  release,
1759
1943
  "release-action": releaseAction
1760
1944
  };