@strapi/content-releases 0.0.0-next.aa7c7ec6724534e157d8a23fe85ee8318dabbf37 → 0.0.0-next.bbb5d9ccd74376825743893bf92696f0e4e6cfb7
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-HjWtUYmc.js} +639 -380
- package/dist/_chunks/App-HjWtUYmc.js.map +1 -0
- package/dist/_chunks/{App-8FCxPK8-.mjs → App-gu1aiP6i.mjs} +650 -392
- package/dist/_chunks/App-gu1aiP6i.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-HrREghh3.js} +24 -5
- package/dist/_chunks/en-HrREghh3.js.map +1 -0
- package/dist/_chunks/{en-m9eTk4UF.mjs → en-ltT1TlKQ.mjs} +24 -5
- package/dist/_chunks/en-ltT1TlKQ.mjs.map +1 -0
- package/dist/_chunks/{index-nGaPcY9m.js → index-ZNwxYN8H.js} +397 -19
- package/dist/_chunks/index-ZNwxYN8H.js.map +1 -0
- package/dist/_chunks/{index-8aK7GzI5.mjs → index-mvj9PSKd.mjs} +413 -35
- package/dist/_chunks/index-mvj9PSKd.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +2 -2
- package/dist/server/index.js +953 -443
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +952 -443
- package/dist/server/index.mjs.map +1 -1
- package/package.json +14 -12
- 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,202 @@ 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
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
215
|
+
if (!i18nPlugin) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
for (const uid in contentTypes2) {
|
|
219
|
+
if (!oldContentTypes[uid]) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
const oldContentType = oldContentTypes[uid];
|
|
223
|
+
const contentType = contentTypes2[uid];
|
|
224
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
225
|
+
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
226
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
227
|
+
locale: null
|
|
228
|
+
}).where({ contentType: uid }).execute();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function enableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
233
|
+
if (!oldContentTypes) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
237
|
+
if (!i18nPlugin) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
for (const uid in contentTypes2) {
|
|
241
|
+
if (!oldContentTypes[uid]) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const oldContentType = oldContentTypes[uid];
|
|
245
|
+
const contentType = contentTypes2[uid];
|
|
246
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
247
|
+
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
248
|
+
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
249
|
+
const defaultLocale = await getDefaultLocale();
|
|
250
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
251
|
+
locale: defaultLocale
|
|
252
|
+
}).where({ contentType: uid }).execute();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
79
256
|
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
80
257
|
const register = async ({ strapi: strapi2 }) => {
|
|
81
258
|
if (features$2.isEnabled("cms-content-releases")) {
|
|
82
259
|
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);
|
|
260
|
+
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish).register(disableContentTypeLocalized);
|
|
261
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
262
|
+
}
|
|
263
|
+
if (strapi2.plugin("graphql")) {
|
|
264
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
265
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
266
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
85
267
|
}
|
|
86
|
-
};
|
|
87
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
88
|
-
return strapi2.plugin("content-releases").service(name);
|
|
89
268
|
};
|
|
90
269
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
91
270
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
92
271
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
272
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
273
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
274
|
+
);
|
|
93
275
|
strapi2.db.lifecycles.subscribe({
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
276
|
+
models: contentTypesWithDraftAndPublish,
|
|
277
|
+
async afterDelete(event) {
|
|
278
|
+
try {
|
|
279
|
+
const { model, result } = event;
|
|
280
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
281
|
+
const { id } = result;
|
|
282
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
283
|
+
where: {
|
|
284
|
+
actions: {
|
|
285
|
+
target_type: model.uid,
|
|
286
|
+
target_id: id
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
291
|
+
where: {
|
|
292
|
+
target_type: model.uid,
|
|
293
|
+
target_id: id
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
for (const release2 of releases) {
|
|
297
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
102
298
|
}
|
|
103
|
-
}
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
104
302
|
}
|
|
105
303
|
},
|
|
106
304
|
/**
|
|
@@ -120,38 +318,94 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
120
318
|
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
121
319
|
*/
|
|
122
320
|
async afterDeleteMany(event) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
321
|
+
try {
|
|
322
|
+
const { model, state } = event;
|
|
323
|
+
const entriesToDelete = state.entriesToDelete;
|
|
324
|
+
if (entriesToDelete) {
|
|
325
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
326
|
+
where: {
|
|
327
|
+
actions: {
|
|
328
|
+
target_type: model.uid,
|
|
329
|
+
target_id: {
|
|
330
|
+
$in: entriesToDelete.map(
|
|
331
|
+
(entry) => entry.id
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
338
|
+
where: {
|
|
339
|
+
target_type: model.uid,
|
|
340
|
+
target_id: {
|
|
341
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
342
|
+
}
|
|
131
343
|
}
|
|
344
|
+
});
|
|
345
|
+
for (const release2 of releases) {
|
|
346
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
132
347
|
}
|
|
348
|
+
}
|
|
349
|
+
} catch (error) {
|
|
350
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
351
|
+
error
|
|
133
352
|
});
|
|
134
353
|
}
|
|
354
|
+
},
|
|
355
|
+
async afterUpdate(event) {
|
|
356
|
+
try {
|
|
357
|
+
const { model, result } = event;
|
|
358
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
359
|
+
const isEntryValid = await getEntryValidStatus(
|
|
360
|
+
model.uid,
|
|
361
|
+
result,
|
|
362
|
+
{
|
|
363
|
+
strapi: strapi2
|
|
364
|
+
}
|
|
365
|
+
);
|
|
366
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
367
|
+
where: {
|
|
368
|
+
target_type: model.uid,
|
|
369
|
+
target_id: result.id
|
|
370
|
+
},
|
|
371
|
+
data: {
|
|
372
|
+
isEntryValid
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
376
|
+
where: {
|
|
377
|
+
actions: {
|
|
378
|
+
target_type: model.uid,
|
|
379
|
+
target_id: result.id
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
for (const release2 of releases) {
|
|
384
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
} catch (error) {
|
|
388
|
+
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
389
|
+
}
|
|
135
390
|
}
|
|
136
391
|
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
392
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
393
|
+
strapi2.log.error(
|
|
394
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
395
|
+
);
|
|
396
|
+
throw err;
|
|
397
|
+
});
|
|
398
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
399
|
+
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
400
|
+
});
|
|
145
401
|
}
|
|
146
402
|
};
|
|
147
403
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
job.cancel();
|
|
154
|
-
}
|
|
404
|
+
const scheduledJobs = getService("scheduling", {
|
|
405
|
+
strapi: strapi2
|
|
406
|
+
}).getAll();
|
|
407
|
+
for (const [, job] of scheduledJobs) {
|
|
408
|
+
job.cancel();
|
|
155
409
|
}
|
|
156
410
|
};
|
|
157
411
|
const schema$1 = {
|
|
@@ -183,6 +437,14 @@ const schema$1 = {
|
|
|
183
437
|
scheduledAt: {
|
|
184
438
|
type: "datetime"
|
|
185
439
|
},
|
|
440
|
+
timezone: {
|
|
441
|
+
type: "string"
|
|
442
|
+
},
|
|
443
|
+
status: {
|
|
444
|
+
type: "enumeration",
|
|
445
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
446
|
+
required: true
|
|
447
|
+
},
|
|
186
448
|
actions: {
|
|
187
449
|
type: "relation",
|
|
188
450
|
relation: "oneToMany",
|
|
@@ -235,6 +497,9 @@ const schema = {
|
|
|
235
497
|
relation: "manyToOne",
|
|
236
498
|
target: RELEASE_MODEL_UID,
|
|
237
499
|
inversedBy: "actions"
|
|
500
|
+
},
|
|
501
|
+
isEntryValid: {
|
|
502
|
+
type: "boolean"
|
|
238
503
|
}
|
|
239
504
|
}
|
|
240
505
|
};
|
|
@@ -257,464 +522,594 @@ const getGroupName = (queryValue) => {
|
|
|
257
522
|
return "contentType.displayName";
|
|
258
523
|
}
|
|
259
524
|
};
|
|
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
|
|
525
|
+
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
526
|
+
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
527
|
+
strapi2.eventHub.emit(event, {
|
|
528
|
+
isPublished,
|
|
529
|
+
error,
|
|
530
|
+
release: release2
|
|
275
531
|
});
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
532
|
+
};
|
|
533
|
+
const publishSingleTypeAction = async (uid, actionType, entryId) => {
|
|
534
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
535
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
536
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
537
|
+
const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
|
|
538
|
+
try {
|
|
539
|
+
if (actionType === "publish") {
|
|
540
|
+
await entityManagerService.publish(entry, uid);
|
|
541
|
+
} else {
|
|
542
|
+
await entityManagerService.unpublish(entry, uid);
|
|
543
|
+
}
|
|
544
|
+
} catch (error) {
|
|
545
|
+
if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
546
|
+
;
|
|
547
|
+
else {
|
|
548
|
+
throw error;
|
|
549
|
+
}
|
|
279
550
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
551
|
+
};
|
|
552
|
+
const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
|
|
553
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
554
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
555
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
556
|
+
const entriesToPublish = await strapi2.entityService.findMany(uid, {
|
|
557
|
+
filters: {
|
|
558
|
+
id: {
|
|
559
|
+
$in: entriesToPublishIds
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
populate
|
|
285
563
|
});
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
...query,
|
|
291
|
-
populate: {
|
|
292
|
-
actions: {
|
|
293
|
-
// @ts-expect-error Ignore missing properties
|
|
294
|
-
count: true
|
|
564
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
|
|
565
|
+
filters: {
|
|
566
|
+
id: {
|
|
567
|
+
$in: entriestoUnpublishIds
|
|
295
568
|
}
|
|
296
|
-
}
|
|
569
|
+
},
|
|
570
|
+
populate
|
|
297
571
|
});
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
572
|
+
if (entriesToPublish.length > 0) {
|
|
573
|
+
await entityManagerService.publishMany(entriesToPublish, uid);
|
|
574
|
+
}
|
|
575
|
+
if (entriesToUnpublish.length > 0) {
|
|
576
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, uid);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
const getFormattedActions = async (releaseId) => {
|
|
580
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
301
581
|
where: {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
target_id: entryId
|
|
305
|
-
},
|
|
306
|
-
releasedAt: {
|
|
307
|
-
$null: true
|
|
582
|
+
release: {
|
|
583
|
+
id: releaseId
|
|
308
584
|
}
|
|
309
585
|
},
|
|
310
586
|
populate: {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
where: {
|
|
314
|
-
target_type: contentTypeUid,
|
|
315
|
-
target_id: entryId
|
|
316
|
-
}
|
|
587
|
+
entry: {
|
|
588
|
+
fields: ["id"]
|
|
317
589
|
}
|
|
318
590
|
}
|
|
319
591
|
});
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
592
|
+
if (actions.length === 0) {
|
|
593
|
+
throw new errors.ValidationError("No entries to publish");
|
|
594
|
+
}
|
|
595
|
+
const collectionTypeActions = {};
|
|
596
|
+
const singleTypeActions = [];
|
|
597
|
+
for (const action of actions) {
|
|
598
|
+
const contentTypeUid = action.contentType;
|
|
599
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
600
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
601
|
+
collectionTypeActions[contentTypeUid] = {
|
|
602
|
+
entriesToPublishIds: [],
|
|
603
|
+
entriesToUnpublishIds: []
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
if (action.type === "publish") {
|
|
607
|
+
collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
|
|
608
|
+
} else {
|
|
609
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
610
|
+
}
|
|
611
|
+
} else {
|
|
612
|
+
singleTypeActions.push({
|
|
613
|
+
uid: contentTypeUid,
|
|
614
|
+
action: action.type,
|
|
615
|
+
id: action.entry.id
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return { collectionTypeActions, singleTypeActions };
|
|
620
|
+
};
|
|
621
|
+
return {
|
|
622
|
+
async create(releaseData, { user }) {
|
|
623
|
+
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
624
|
+
const {
|
|
625
|
+
validatePendingReleasesLimit,
|
|
626
|
+
validateUniqueNameForPendingRelease,
|
|
627
|
+
validateScheduledAtIsLaterThanNow
|
|
628
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
629
|
+
await Promise.all([
|
|
630
|
+
validatePendingReleasesLimit(),
|
|
631
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
632
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
633
|
+
]);
|
|
634
|
+
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
635
|
+
data: {
|
|
636
|
+
...releaseWithCreatorFields,
|
|
637
|
+
status: "empty"
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
641
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
642
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
328
643
|
}
|
|
644
|
+
strapi2.telemetry.send("didCreateContentRelease");
|
|
329
645
|
return release2;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
646
|
+
},
|
|
647
|
+
async findOne(id, query = {}) {
|
|
648
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
649
|
+
...query
|
|
650
|
+
});
|
|
651
|
+
return release2;
|
|
652
|
+
},
|
|
653
|
+
findPage(query) {
|
|
654
|
+
return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
|
|
655
|
+
...query,
|
|
656
|
+
populate: {
|
|
657
|
+
actions: {
|
|
658
|
+
// @ts-expect-error Ignore missing properties
|
|
659
|
+
count: true
|
|
660
|
+
}
|
|
341
661
|
}
|
|
662
|
+
});
|
|
663
|
+
},
|
|
664
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
|
|
665
|
+
let entries = entriesIds;
|
|
666
|
+
if (!Array.isArray(entriesIds)) {
|
|
667
|
+
entries = [entriesIds];
|
|
342
668
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
669
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
670
|
+
where: {
|
|
671
|
+
actions: {
|
|
672
|
+
target_type: contentTypeUid,
|
|
673
|
+
target_id: {
|
|
674
|
+
$in: entries
|
|
350
675
|
}
|
|
351
676
|
},
|
|
352
|
-
{
|
|
353
|
-
|
|
677
|
+
releasedAt: {
|
|
678
|
+
$null: true
|
|
354
679
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
680
|
+
},
|
|
681
|
+
populate: {
|
|
682
|
+
// Filter the action to get only the content type entry
|
|
683
|
+
actions: {
|
|
684
|
+
where: {
|
|
685
|
+
target_type: contentTypeUid,
|
|
686
|
+
target_id: {
|
|
687
|
+
$in: entries
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
populate: {
|
|
691
|
+
entry: {
|
|
692
|
+
select: ["id"]
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
return releases.map((release2) => {
|
|
699
|
+
if (release2.actions?.length) {
|
|
700
|
+
const actionsForEntry = release2.actions;
|
|
701
|
+
delete release2.actions;
|
|
702
|
+
return {
|
|
703
|
+
...release2,
|
|
704
|
+
actions: actionsForEntry
|
|
705
|
+
};
|
|
358
706
|
}
|
|
707
|
+
return release2;
|
|
708
|
+
});
|
|
709
|
+
},
|
|
710
|
+
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
711
|
+
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
712
|
+
where: {
|
|
713
|
+
releasedAt: {
|
|
714
|
+
$null: true
|
|
715
|
+
},
|
|
716
|
+
actions: {
|
|
717
|
+
target_type: contentTypeUid,
|
|
718
|
+
target_id: entryId
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
723
|
+
where: {
|
|
724
|
+
$or: [
|
|
725
|
+
{
|
|
726
|
+
id: {
|
|
727
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
actions: null
|
|
732
|
+
}
|
|
733
|
+
],
|
|
734
|
+
releasedAt: {
|
|
735
|
+
$null: true
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
return releases.map((release2) => {
|
|
740
|
+
if (release2.actions?.length) {
|
|
741
|
+
const [actionForEntry] = release2.actions;
|
|
742
|
+
delete release2.actions;
|
|
743
|
+
return {
|
|
744
|
+
...release2,
|
|
745
|
+
action: actionForEntry
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
return release2;
|
|
749
|
+
});
|
|
750
|
+
},
|
|
751
|
+
async update(id, releaseData, { user }) {
|
|
752
|
+
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(
|
|
753
|
+
releaseData
|
|
754
|
+
);
|
|
755
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
756
|
+
"release-validation",
|
|
757
|
+
{ strapi: strapi2 }
|
|
758
|
+
);
|
|
759
|
+
await Promise.all([
|
|
760
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
761
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
762
|
+
]);
|
|
763
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
764
|
+
if (!release2) {
|
|
765
|
+
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
359
766
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
if (release2.actions?.length) {
|
|
363
|
-
const [actionForEntry] = release2.actions;
|
|
364
|
-
delete release2.actions;
|
|
365
|
-
return {
|
|
366
|
-
...release2,
|
|
367
|
-
action: actionForEntry
|
|
368
|
-
};
|
|
767
|
+
if (release2.releasedAt) {
|
|
768
|
+
throw new errors.ValidationError("Release already published");
|
|
369
769
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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")) {
|
|
770
|
+
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
771
|
+
/*
|
|
772
|
+
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
773
|
+
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
774
|
+
*/
|
|
775
|
+
// @ts-expect-error see above
|
|
776
|
+
data: releaseWithCreatorFields
|
|
777
|
+
});
|
|
399
778
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
400
779
|
if (releaseData.scheduledAt) {
|
|
401
780
|
await schedulingService.set(id, releaseData.scheduledAt);
|
|
402
781
|
} else if (release2.scheduledAt) {
|
|
403
782
|
schedulingService.cancel(id);
|
|
404
783
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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: "*"
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
filters: {
|
|
454
|
-
release: releaseId
|
|
784
|
+
this.updateReleaseStatus(id);
|
|
785
|
+
strapi2.telemetry.send("didUpdateContentRelease");
|
|
786
|
+
return updatedRelease;
|
|
787
|
+
},
|
|
788
|
+
async createAction(releaseId, action) {
|
|
789
|
+
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
790
|
+
strapi: strapi2
|
|
791
|
+
});
|
|
792
|
+
await Promise.all([
|
|
793
|
+
validateEntryContentType(action.entry.contentType),
|
|
794
|
+
validateUniqueEntry(releaseId, action)
|
|
795
|
+
]);
|
|
796
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
797
|
+
if (!release2) {
|
|
798
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
455
799
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
async countActions(query) {
|
|
459
|
-
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
460
|
-
},
|
|
461
|
-
async groupActions(actions, groupBy) {
|
|
462
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
463
|
-
if (!acc.includes(action.contentType)) {
|
|
464
|
-
acc.push(action.contentType);
|
|
800
|
+
if (release2.releasedAt) {
|
|
801
|
+
throw new errors.ValidationError("Release already published");
|
|
465
802
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
});
|
|
484
|
-
const groupName = getGroupName(groupBy);
|
|
485
|
-
return _.groupBy(groupName)(formattedData);
|
|
486
|
-
},
|
|
487
|
-
async getLocalesDataForActions() {
|
|
488
|
-
if (!strapi2.plugin("i18n")) {
|
|
489
|
-
return {};
|
|
490
|
-
}
|
|
491
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
492
|
-
return allLocales.reduce((acc, locale) => {
|
|
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
|
|
803
|
+
const { entry, type } = action;
|
|
804
|
+
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
805
|
+
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
806
|
+
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
807
|
+
data: {
|
|
808
|
+
type,
|
|
809
|
+
contentType: entry.contentType,
|
|
810
|
+
locale: entry.locale,
|
|
811
|
+
isEntryValid,
|
|
812
|
+
entry: {
|
|
813
|
+
id: entry.id,
|
|
814
|
+
__type: entry.contentType,
|
|
815
|
+
__pivot: { field: "entry" }
|
|
816
|
+
},
|
|
817
|
+
release: releaseId
|
|
818
|
+
},
|
|
819
|
+
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
503
820
|
});
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (!acc.includes(action.contentType)) {
|
|
514
|
-
acc.push(action.contentType);
|
|
821
|
+
this.updateReleaseStatus(releaseId);
|
|
822
|
+
return releaseAction2;
|
|
823
|
+
},
|
|
824
|
+
async findActions(releaseId, query) {
|
|
825
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
826
|
+
fields: ["id"]
|
|
827
|
+
});
|
|
828
|
+
if (!release2) {
|
|
829
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
515
830
|
}
|
|
516
|
-
return
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
831
|
+
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
832
|
+
...query,
|
|
833
|
+
populate: {
|
|
834
|
+
entry: {
|
|
835
|
+
populate: "*"
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
filters: {
|
|
839
|
+
release: releaseId
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
},
|
|
843
|
+
async countActions(query) {
|
|
844
|
+
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
845
|
+
},
|
|
846
|
+
async groupActions(actions, groupBy) {
|
|
847
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
848
|
+
if (!acc.includes(action.contentType)) {
|
|
849
|
+
acc.push(action.contentType);
|
|
850
|
+
}
|
|
521
851
|
return acc;
|
|
522
|
-
},
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
852
|
+
}, []);
|
|
853
|
+
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
854
|
+
contentTypeUids
|
|
855
|
+
);
|
|
856
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
857
|
+
const formattedData = actions.map((action) => {
|
|
858
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
859
|
+
return {
|
|
860
|
+
...action,
|
|
861
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
862
|
+
contentType: {
|
|
863
|
+
displayName,
|
|
864
|
+
mainFieldValue: action.entry[mainField],
|
|
865
|
+
uid: action.contentType
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
});
|
|
869
|
+
const groupName = getGroupName(groupBy);
|
|
870
|
+
return _.groupBy(groupName)(formattedData);
|
|
871
|
+
},
|
|
872
|
+
async getLocalesDataForActions() {
|
|
873
|
+
if (!strapi2.plugin("i18n")) {
|
|
874
|
+
return {};
|
|
875
|
+
}
|
|
876
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
877
|
+
return allLocales.reduce((acc, locale) => {
|
|
878
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
533
879
|
return acc;
|
|
534
|
-
},
|
|
535
|
-
|
|
536
|
-
)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
880
|
+
}, {});
|
|
881
|
+
},
|
|
882
|
+
async getContentTypesDataForActions(contentTypesUids) {
|
|
883
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
884
|
+
const contentTypesData = {};
|
|
885
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
886
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
887
|
+
uid: contentTypeUid
|
|
888
|
+
});
|
|
889
|
+
contentTypesData[contentTypeUid] = {
|
|
890
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
891
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
892
|
+
};
|
|
545
893
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
}
|
|
894
|
+
return contentTypesData;
|
|
895
|
+
},
|
|
896
|
+
getContentTypeModelsFromActions(actions) {
|
|
897
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
898
|
+
if (!acc.includes(action.contentType)) {
|
|
899
|
+
acc.push(action.contentType);
|
|
559
900
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
901
|
+
return acc;
|
|
902
|
+
}, []);
|
|
903
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
904
|
+
(acc, contentTypeUid) => {
|
|
905
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
906
|
+
return acc;
|
|
907
|
+
},
|
|
908
|
+
{}
|
|
909
|
+
);
|
|
910
|
+
return contentTypeModelsMap;
|
|
911
|
+
},
|
|
912
|
+
async getAllComponents() {
|
|
913
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
914
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
915
|
+
const componentsMap = components.reduce(
|
|
916
|
+
(acc, component) => {
|
|
917
|
+
acc[component.uid] = component;
|
|
918
|
+
return acc;
|
|
919
|
+
},
|
|
920
|
+
{}
|
|
921
|
+
);
|
|
922
|
+
return componentsMap;
|
|
923
|
+
},
|
|
924
|
+
async delete(releaseId) {
|
|
925
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
570
926
|
populate: {
|
|
571
927
|
actions: {
|
|
572
|
-
|
|
573
|
-
entry: {
|
|
574
|
-
fields: ["id"]
|
|
575
|
-
}
|
|
576
|
-
}
|
|
928
|
+
fields: ["id"]
|
|
577
929
|
}
|
|
578
930
|
}
|
|
931
|
+
});
|
|
932
|
+
if (!release2) {
|
|
933
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
579
934
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const collectionTypeActions = {};
|
|
591
|
-
const singleTypeActions = [];
|
|
592
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
593
|
-
const contentTypeUid = action.contentType;
|
|
594
|
-
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
595
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
596
|
-
collectionTypeActions[contentTypeUid] = {
|
|
597
|
-
entriestoPublishIds: [],
|
|
598
|
-
entriesToUnpublishIds: []
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
if (action.type === "publish") {
|
|
602
|
-
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
603
|
-
} else {
|
|
604
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
605
|
-
}
|
|
606
|
-
} else {
|
|
607
|
-
singleTypeActions.push({
|
|
608
|
-
uid: contentTypeUid,
|
|
609
|
-
action: action.type,
|
|
610
|
-
id: action.entry.id
|
|
935
|
+
if (release2.releasedAt) {
|
|
936
|
+
throw new errors.ValidationError("Release already published");
|
|
937
|
+
}
|
|
938
|
+
await strapi2.db.transaction(async () => {
|
|
939
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
940
|
+
where: {
|
|
941
|
+
id: {
|
|
942
|
+
$in: release2.actions.map((action) => action.id)
|
|
943
|
+
}
|
|
944
|
+
}
|
|
611
945
|
});
|
|
946
|
+
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
947
|
+
});
|
|
948
|
+
if (release2.scheduledAt) {
|
|
949
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
950
|
+
await schedulingService.cancel(release2.id);
|
|
612
951
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
952
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
953
|
+
return release2;
|
|
954
|
+
},
|
|
955
|
+
async publish(releaseId) {
|
|
956
|
+
const {
|
|
957
|
+
release: release2,
|
|
958
|
+
error
|
|
959
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
960
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
961
|
+
if (!lockedRelease) {
|
|
962
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
963
|
+
}
|
|
964
|
+
if (lockedRelease.releasedAt) {
|
|
965
|
+
throw new errors.ValidationError("Release already published");
|
|
966
|
+
}
|
|
967
|
+
if (lockedRelease.status === "failed") {
|
|
968
|
+
throw new errors.ValidationError("Release failed to publish");
|
|
969
|
+
}
|
|
620
970
|
try {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
971
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
972
|
+
const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
|
|
973
|
+
releaseId
|
|
974
|
+
);
|
|
975
|
+
await strapi2.db.transaction(async () => {
|
|
976
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
977
|
+
await publishSingleTypeAction(uid, action, id);
|
|
978
|
+
}
|
|
979
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
980
|
+
const uid = contentTypeUid;
|
|
981
|
+
await publishCollectionTypeAction(
|
|
982
|
+
uid,
|
|
983
|
+
collectionTypeActions[uid].entriesToPublishIds,
|
|
984
|
+
collectionTypeActions[uid].entriesToUnpublishIds
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
989
|
+
where: {
|
|
990
|
+
id: releaseId
|
|
991
|
+
},
|
|
992
|
+
data: {
|
|
993
|
+
status: "done",
|
|
994
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
998
|
+
isPublished: true,
|
|
999
|
+
release: release22
|
|
1000
|
+
});
|
|
1001
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
1002
|
+
return { release: release22, error: null };
|
|
1003
|
+
} catch (error2) {
|
|
1004
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
1005
|
+
isPublished: false,
|
|
1006
|
+
error: error2
|
|
1007
|
+
});
|
|
1008
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
1009
|
+
status: "failed"
|
|
1010
|
+
}).transacting(trx).execute();
|
|
1011
|
+
return {
|
|
1012
|
+
release: null,
|
|
1013
|
+
error: error2
|
|
1014
|
+
};
|
|
632
1015
|
}
|
|
1016
|
+
});
|
|
1017
|
+
if (error) {
|
|
1018
|
+
throw error;
|
|
633
1019
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
populate
|
|
1020
|
+
return release2;
|
|
1021
|
+
},
|
|
1022
|
+
async updateAction(actionId, releaseId, update) {
|
|
1023
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1024
|
+
where: {
|
|
1025
|
+
id: actionId,
|
|
1026
|
+
release: {
|
|
1027
|
+
id: releaseId,
|
|
1028
|
+
releasedAt: {
|
|
1029
|
+
$null: true
|
|
1030
|
+
}
|
|
646
1031
|
}
|
|
1032
|
+
},
|
|
1033
|
+
data: update
|
|
1034
|
+
});
|
|
1035
|
+
if (!updatedAction) {
|
|
1036
|
+
throw new errors.NotFoundError(
|
|
1037
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
647
1038
|
);
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
1039
|
+
}
|
|
1040
|
+
return updatedAction;
|
|
1041
|
+
},
|
|
1042
|
+
async deleteAction(actionId, releaseId) {
|
|
1043
|
+
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1044
|
+
where: {
|
|
1045
|
+
id: actionId,
|
|
1046
|
+
release: {
|
|
1047
|
+
id: releaseId,
|
|
1048
|
+
releasedAt: {
|
|
1049
|
+
$null: true
|
|
1050
|
+
}
|
|
657
1051
|
}
|
|
658
|
-
);
|
|
659
|
-
if (entriesToPublish.length > 0) {
|
|
660
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
661
|
-
}
|
|
662
|
-
if (entriesToUnpublish.length > 0) {
|
|
663
|
-
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
664
1052
|
}
|
|
1053
|
+
});
|
|
1054
|
+
if (!deletedAction) {
|
|
1055
|
+
throw new errors.NotFoundError(
|
|
1056
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1057
|
+
);
|
|
665
1058
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
return release2;
|
|
677
|
-
},
|
|
678
|
-
async updateAction(actionId, releaseId, update) {
|
|
679
|
-
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
680
|
-
where: {
|
|
681
|
-
id: actionId,
|
|
682
|
-
release: {
|
|
683
|
-
id: releaseId,
|
|
684
|
-
releasedAt: {
|
|
685
|
-
$null: true
|
|
1059
|
+
this.updateReleaseStatus(releaseId);
|
|
1060
|
+
return deletedAction;
|
|
1061
|
+
},
|
|
1062
|
+
async updateReleaseStatus(releaseId) {
|
|
1063
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
1064
|
+
this.countActions({
|
|
1065
|
+
filters: {
|
|
1066
|
+
release: releaseId
|
|
686
1067
|
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
throw new errors.NotFoundError(
|
|
693
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
694
|
-
);
|
|
695
|
-
}
|
|
696
|
-
return updatedAction;
|
|
697
|
-
},
|
|
698
|
-
async deleteAction(actionId, releaseId) {
|
|
699
|
-
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
700
|
-
where: {
|
|
701
|
-
id: actionId,
|
|
702
|
-
release: {
|
|
703
|
-
id: releaseId,
|
|
704
|
-
releasedAt: {
|
|
705
|
-
$null: true
|
|
1068
|
+
}),
|
|
1069
|
+
this.countActions({
|
|
1070
|
+
filters: {
|
|
1071
|
+
release: releaseId,
|
|
1072
|
+
isEntryValid: false
|
|
706
1073
|
}
|
|
1074
|
+
})
|
|
1075
|
+
]);
|
|
1076
|
+
if (totalActions > 0) {
|
|
1077
|
+
if (invalidActions > 0) {
|
|
1078
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1079
|
+
where: {
|
|
1080
|
+
id: releaseId
|
|
1081
|
+
},
|
|
1082
|
+
data: {
|
|
1083
|
+
status: "blocked"
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
707
1086
|
}
|
|
1087
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1088
|
+
where: {
|
|
1089
|
+
id: releaseId
|
|
1090
|
+
},
|
|
1091
|
+
data: {
|
|
1092
|
+
status: "ready"
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
708
1095
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
1096
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1097
|
+
where: {
|
|
1098
|
+
id: releaseId
|
|
1099
|
+
},
|
|
1100
|
+
data: {
|
|
1101
|
+
status: "empty"
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
714
1104
|
}
|
|
715
|
-
|
|
1105
|
+
};
|
|
1106
|
+
};
|
|
1107
|
+
class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
1108
|
+
constructor(message) {
|
|
1109
|
+
super(message);
|
|
1110
|
+
this.name = "AlreadyOnReleaseError";
|
|
716
1111
|
}
|
|
717
|
-
}
|
|
1112
|
+
}
|
|
718
1113
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
719
1114
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
720
1115
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -727,7 +1122,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
727
1122
|
(action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
|
|
728
1123
|
);
|
|
729
1124
|
if (isEntryInRelease) {
|
|
730
|
-
throw new
|
|
1125
|
+
throw new AlreadyOnReleaseError(
|
|
731
1126
|
`Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
|
|
732
1127
|
);
|
|
733
1128
|
}
|
|
@@ -835,12 +1230,27 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
835
1230
|
const services = {
|
|
836
1231
|
release: createReleaseService,
|
|
837
1232
|
"release-validation": createReleaseValidationService,
|
|
838
|
-
|
|
1233
|
+
scheduling: createSchedulingService
|
|
839
1234
|
};
|
|
840
1235
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
841
1236
|
name: yup.string().trim().required(),
|
|
842
|
-
|
|
843
|
-
|
|
1237
|
+
scheduledAt: yup.string().nullable(),
|
|
1238
|
+
isScheduled: yup.boolean().optional(),
|
|
1239
|
+
time: yup.string().when("isScheduled", {
|
|
1240
|
+
is: true,
|
|
1241
|
+
then: yup.string().trim().required(),
|
|
1242
|
+
otherwise: yup.string().nullable()
|
|
1243
|
+
}),
|
|
1244
|
+
timezone: yup.string().when("isScheduled", {
|
|
1245
|
+
is: true,
|
|
1246
|
+
then: yup.string().required().nullable(),
|
|
1247
|
+
otherwise: yup.string().nullable()
|
|
1248
|
+
}),
|
|
1249
|
+
date: yup.string().when("isScheduled", {
|
|
1250
|
+
is: true,
|
|
1251
|
+
then: yup.string().required().nullable(),
|
|
1252
|
+
otherwise: yup.string().nullable()
|
|
1253
|
+
})
|
|
844
1254
|
}).required().noUnknown();
|
|
845
1255
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
846
1256
|
const releaseController = {
|
|
@@ -873,7 +1283,12 @@ const releaseController = {
|
|
|
873
1283
|
}
|
|
874
1284
|
};
|
|
875
1285
|
});
|
|
876
|
-
|
|
1286
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1287
|
+
where: {
|
|
1288
|
+
releasedAt: null
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
877
1292
|
}
|
|
878
1293
|
},
|
|
879
1294
|
async findOne(ctx) {
|
|
@@ -902,6 +1317,33 @@ const releaseController = {
|
|
|
902
1317
|
};
|
|
903
1318
|
ctx.body = { data };
|
|
904
1319
|
},
|
|
1320
|
+
async mapEntriesToReleases(ctx) {
|
|
1321
|
+
const { contentTypeUid, entriesIds } = ctx.query;
|
|
1322
|
+
if (!contentTypeUid || !entriesIds) {
|
|
1323
|
+
throw new errors.ValidationError("Missing required query parameters");
|
|
1324
|
+
}
|
|
1325
|
+
const releaseService = getService("release", { strapi });
|
|
1326
|
+
const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
|
|
1327
|
+
contentTypeUid,
|
|
1328
|
+
entriesIds
|
|
1329
|
+
);
|
|
1330
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1331
|
+
(acc, release2) => {
|
|
1332
|
+
release2.actions.forEach((action) => {
|
|
1333
|
+
if (!acc[action.entry.id]) {
|
|
1334
|
+
acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
|
|
1335
|
+
} else {
|
|
1336
|
+
acc[action.entry.id].push({ id: release2.id, name: release2.name });
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1339
|
+
return acc;
|
|
1340
|
+
},
|
|
1341
|
+
{}
|
|
1342
|
+
);
|
|
1343
|
+
ctx.body = {
|
|
1344
|
+
data: mappedEntriesInReleases
|
|
1345
|
+
};
|
|
1346
|
+
},
|
|
905
1347
|
async create(ctx) {
|
|
906
1348
|
const user = ctx.state.user;
|
|
907
1349
|
const releaseArgs = ctx.request.body;
|
|
@@ -991,6 +1433,38 @@ const releaseActionController = {
|
|
|
991
1433
|
data: releaseAction2
|
|
992
1434
|
};
|
|
993
1435
|
},
|
|
1436
|
+
async createMany(ctx) {
|
|
1437
|
+
const releaseId = ctx.params.releaseId;
|
|
1438
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1439
|
+
await Promise.all(
|
|
1440
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1441
|
+
);
|
|
1442
|
+
const releaseService = getService("release", { strapi });
|
|
1443
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1444
|
+
const releaseActions2 = await Promise.all(
|
|
1445
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1446
|
+
try {
|
|
1447
|
+
const action = await releaseService.createAction(releaseId, releaseActionArgs);
|
|
1448
|
+
return action;
|
|
1449
|
+
} catch (error) {
|
|
1450
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
throw error;
|
|
1454
|
+
}
|
|
1455
|
+
})
|
|
1456
|
+
);
|
|
1457
|
+
return releaseActions2;
|
|
1458
|
+
});
|
|
1459
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1460
|
+
ctx.body = {
|
|
1461
|
+
data: newReleaseActions,
|
|
1462
|
+
meta: {
|
|
1463
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1464
|
+
totalEntries: releaseActions.length
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
},
|
|
994
1468
|
async findMany(ctx) {
|
|
995
1469
|
const releaseId = ctx.params.releaseId;
|
|
996
1470
|
const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
@@ -1059,6 +1533,22 @@ const controllers = { release: releaseController, "release-action": releaseActio
|
|
|
1059
1533
|
const release = {
|
|
1060
1534
|
type: "admin",
|
|
1061
1535
|
routes: [
|
|
1536
|
+
{
|
|
1537
|
+
method: "GET",
|
|
1538
|
+
path: "/mapEntriesToReleases",
|
|
1539
|
+
handler: "release.mapEntriesToReleases",
|
|
1540
|
+
config: {
|
|
1541
|
+
policies: [
|
|
1542
|
+
"admin::isAuthenticatedAdmin",
|
|
1543
|
+
{
|
|
1544
|
+
name: "admin::hasPermissions",
|
|
1545
|
+
config: {
|
|
1546
|
+
actions: ["plugin::content-releases.read"]
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
]
|
|
1550
|
+
}
|
|
1551
|
+
},
|
|
1062
1552
|
{
|
|
1063
1553
|
method: "POST",
|
|
1064
1554
|
path: "/",
|
|
@@ -1176,6 +1666,22 @@ const releaseAction = {
|
|
|
1176
1666
|
]
|
|
1177
1667
|
}
|
|
1178
1668
|
},
|
|
1669
|
+
{
|
|
1670
|
+
method: "POST",
|
|
1671
|
+
path: "/:releaseId/actions/bulk",
|
|
1672
|
+
handler: "release-action.createMany",
|
|
1673
|
+
config: {
|
|
1674
|
+
policies: [
|
|
1675
|
+
"admin::isAuthenticatedAdmin",
|
|
1676
|
+
{
|
|
1677
|
+
name: "admin::hasPermissions",
|
|
1678
|
+
config: {
|
|
1679
|
+
actions: ["plugin::content-releases.create-action"]
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
]
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1179
1685
|
{
|
|
1180
1686
|
method: "GET",
|
|
1181
1687
|
path: "/:releaseId/actions",
|
|
@@ -1244,6 +1750,9 @@ const getPlugin = () => {
|
|
|
1244
1750
|
};
|
|
1245
1751
|
}
|
|
1246
1752
|
return {
|
|
1753
|
+
// Always return register, it handles its own feature check
|
|
1754
|
+
register,
|
|
1755
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1247
1756
|
contentTypes
|
|
1248
1757
|
};
|
|
1249
1758
|
};
|