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