@strapi/content-releases 0.0.0-next.f8af92b375dc730ba47ed2117f25df893aae696c → 0.0.0-next.fe88c7878fe2948cfdc68f4867329c1032a618b2
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/LICENSE +17 -1
- package/dist/_chunks/{App-OK4Xac-O.js → App-dLXY5ei3.js} +677 -639
- package/dist/_chunks/App-dLXY5ei3.js.map +1 -0
- package/dist/_chunks/{App-xAkiD42p.mjs → App-jrh58sXY.mjs} +690 -652
- package/dist/_chunks/App-jrh58sXY.mjs.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-3tRbmbY3.mjs} +2 -2
- package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-bpIYXOfu.js} +2 -2
- package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
- package/dist/_chunks/{en-r0otWaln.js → en-HrREghh3.js} +14 -5
- package/dist/_chunks/en-HrREghh3.js.map +1 -0
- package/dist/_chunks/{en-veqvqeEr.mjs → en-ltT1TlKQ.mjs} +14 -5
- package/dist/_chunks/en-ltT1TlKQ.mjs.map +1 -0
- package/dist/_chunks/{index-JvA2_26n.js → index-CVO0Rqdm.js} +343 -22
- package/dist/_chunks/index-CVO0Rqdm.js.map +1 -0
- package/dist/_chunks/{index-exoiSU3V.mjs → index-PiOGBETy.mjs} +358 -37
- package/dist/_chunks/index-PiOGBETy.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +621 -175
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +620 -175
- package/dist/server/index.mjs.map +1 -1
- package/package.json +13 -13
- package/dist/_chunks/App-OK4Xac-O.js.map +0 -1
- package/dist/_chunks/App-xAkiD42p.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
- package/dist/_chunks/en-r0otWaln.js.map +0 -1
- package/dist/_chunks/en-veqvqeEr.mjs.map +0 -1
- package/dist/_chunks/index-JvA2_26n.js.map +0 -1
- package/dist/_chunks/index-exoiSU3V.mjs.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";
|
|
@@ -53,6 +54,29 @@ const ACTIONS = [
|
|
|
53
54
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
54
55
|
RELEASES_PUBLISH: "releases.publish"
|
|
55
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
|
+
};
|
|
56
80
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
57
81
|
oldContentTypes,
|
|
58
82
|
contentTypes: contentTypes2
|
|
@@ -79,31 +103,202 @@ async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes:
|
|
|
79
103
|
});
|
|
80
104
|
}
|
|
81
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
|
+
}
|
|
82
256
|
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
83
257
|
const register = async ({ strapi: strapi2 }) => {
|
|
84
258
|
if (features$2.isEnabled("cms-content-releases")) {
|
|
85
259
|
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
86
|
-
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
|
|
87
|
-
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();
|
|
88
267
|
}
|
|
89
|
-
};
|
|
90
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
91
|
-
return strapi2.plugin("content-releases").service(name);
|
|
92
268
|
};
|
|
93
269
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
94
270
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
95
271
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
272
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
273
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
274
|
+
);
|
|
96
275
|
strapi2.db.lifecycles.subscribe({
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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);
|
|
105
298
|
}
|
|
106
|
-
}
|
|
299
|
+
}
|
|
300
|
+
} catch (error) {
|
|
301
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
107
302
|
}
|
|
108
303
|
},
|
|
109
304
|
/**
|
|
@@ -123,41 +318,94 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
123
318
|
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
124
319
|
*/
|
|
125
320
|
async afterDeleteMany(event) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
}
|
|
134
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
|
+
}
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
for (const release2 of releases) {
|
|
346
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
135
347
|
}
|
|
348
|
+
}
|
|
349
|
+
} catch (error) {
|
|
350
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
351
|
+
error
|
|
136
352
|
});
|
|
137
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
|
+
}
|
|
138
390
|
}
|
|
139
391
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
});
|
|
150
|
-
}
|
|
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
|
+
});
|
|
151
401
|
}
|
|
152
402
|
};
|
|
153
403
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
job.cancel();
|
|
160
|
-
}
|
|
404
|
+
const scheduledJobs = getService("scheduling", {
|
|
405
|
+
strapi: strapi2
|
|
406
|
+
}).getAll();
|
|
407
|
+
for (const [, job] of scheduledJobs) {
|
|
408
|
+
job.cancel();
|
|
161
409
|
}
|
|
162
410
|
};
|
|
163
411
|
const schema$1 = {
|
|
@@ -192,6 +440,11 @@ const schema$1 = {
|
|
|
192
440
|
timezone: {
|
|
193
441
|
type: "string"
|
|
194
442
|
},
|
|
443
|
+
status: {
|
|
444
|
+
type: "enumeration",
|
|
445
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
446
|
+
required: true
|
|
447
|
+
},
|
|
195
448
|
actions: {
|
|
196
449
|
type: "relation",
|
|
197
450
|
relation: "oneToMany",
|
|
@@ -244,6 +497,9 @@ const schema = {
|
|
|
244
497
|
relation: "manyToOne",
|
|
245
498
|
target: RELEASE_MODEL_UID,
|
|
246
499
|
inversedBy: "actions"
|
|
500
|
+
},
|
|
501
|
+
isEntryValid: {
|
|
502
|
+
type: "boolean"
|
|
247
503
|
}
|
|
248
504
|
}
|
|
249
505
|
};
|
|
@@ -274,6 +530,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
274
530
|
release: release2
|
|
275
531
|
});
|
|
276
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
|
+
}
|
|
550
|
+
}
|
|
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
|
|
563
|
+
});
|
|
564
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
|
|
565
|
+
filters: {
|
|
566
|
+
id: {
|
|
567
|
+
$in: entriestoUnpublishIds
|
|
568
|
+
}
|
|
569
|
+
},
|
|
570
|
+
populate
|
|
571
|
+
});
|
|
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({
|
|
581
|
+
where: {
|
|
582
|
+
release: {
|
|
583
|
+
id: releaseId
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
populate: {
|
|
587
|
+
entry: {
|
|
588
|
+
fields: ["id"]
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
});
|
|
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
|
+
};
|
|
277
621
|
return {
|
|
278
622
|
async create(releaseData, { user }) {
|
|
279
623
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
@@ -288,9 +632,12 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
288
632
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
289
633
|
]);
|
|
290
634
|
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
291
|
-
data:
|
|
635
|
+
data: {
|
|
636
|
+
...releaseWithCreatorFields,
|
|
637
|
+
status: "empty"
|
|
638
|
+
}
|
|
292
639
|
});
|
|
293
|
-
if (
|
|
640
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
294
641
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
295
642
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
296
643
|
}
|
|
@@ -314,12 +661,18 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
314
661
|
}
|
|
315
662
|
});
|
|
316
663
|
},
|
|
317
|
-
async findManyWithContentTypeEntryAttached(contentTypeUid,
|
|
664
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
|
|
665
|
+
let entries = entriesIds;
|
|
666
|
+
if (!Array.isArray(entriesIds)) {
|
|
667
|
+
entries = [entriesIds];
|
|
668
|
+
}
|
|
318
669
|
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
319
670
|
where: {
|
|
320
671
|
actions: {
|
|
321
672
|
target_type: contentTypeUid,
|
|
322
|
-
target_id:
|
|
673
|
+
target_id: {
|
|
674
|
+
$in: entries
|
|
675
|
+
}
|
|
323
676
|
},
|
|
324
677
|
releasedAt: {
|
|
325
678
|
$null: true
|
|
@@ -330,18 +683,25 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
330
683
|
actions: {
|
|
331
684
|
where: {
|
|
332
685
|
target_type: contentTypeUid,
|
|
333
|
-
target_id:
|
|
686
|
+
target_id: {
|
|
687
|
+
$in: entries
|
|
688
|
+
}
|
|
689
|
+
},
|
|
690
|
+
populate: {
|
|
691
|
+
entry: {
|
|
692
|
+
select: ["id"]
|
|
693
|
+
}
|
|
334
694
|
}
|
|
335
695
|
}
|
|
336
696
|
}
|
|
337
697
|
});
|
|
338
698
|
return releases.map((release2) => {
|
|
339
699
|
if (release2.actions?.length) {
|
|
340
|
-
const
|
|
700
|
+
const actionsForEntry = release2.actions;
|
|
341
701
|
delete release2.actions;
|
|
342
702
|
return {
|
|
343
703
|
...release2,
|
|
344
|
-
|
|
704
|
+
actions: actionsForEntry
|
|
345
705
|
};
|
|
346
706
|
}
|
|
347
707
|
return release2;
|
|
@@ -415,14 +775,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
415
775
|
// @ts-expect-error see above
|
|
416
776
|
data: releaseWithCreatorFields
|
|
417
777
|
});
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
schedulingService.cancel(id);
|
|
424
|
-
}
|
|
778
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
779
|
+
if (releaseData.scheduledAt) {
|
|
780
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
781
|
+
} else if (release2.scheduledAt) {
|
|
782
|
+
schedulingService.cancel(id);
|
|
425
783
|
}
|
|
784
|
+
this.updateReleaseStatus(id);
|
|
426
785
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
427
786
|
return updatedRelease;
|
|
428
787
|
},
|
|
@@ -442,11 +801,14 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
442
801
|
throw new errors.ValidationError("Release already published");
|
|
443
802
|
}
|
|
444
803
|
const { entry, type } = action;
|
|
445
|
-
|
|
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, {
|
|
446
807
|
data: {
|
|
447
808
|
type,
|
|
448
809
|
contentType: entry.contentType,
|
|
449
810
|
locale: entry.locale,
|
|
811
|
+
isEntryValid,
|
|
450
812
|
entry: {
|
|
451
813
|
id: entry.id,
|
|
452
814
|
__type: entry.contentType,
|
|
@@ -456,6 +818,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
456
818
|
},
|
|
457
819
|
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
458
820
|
});
|
|
821
|
+
this.updateReleaseStatus(releaseId);
|
|
822
|
+
return releaseAction2;
|
|
459
823
|
},
|
|
460
824
|
async findActions(releaseId, query) {
|
|
461
825
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -581,7 +945,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
581
945
|
});
|
|
582
946
|
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
583
947
|
});
|
|
584
|
-
if (
|
|
948
|
+
if (release2.scheduledAt) {
|
|
585
949
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
586
950
|
await schedulingService.cancel(release2.id);
|
|
587
951
|
}
|
|
@@ -589,139 +953,71 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
589
953
|
return release2;
|
|
590
954
|
},
|
|
591
955
|
async publish(releaseId) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
actions: {
|
|
599
|
-
populate: {
|
|
600
|
-
entry: {
|
|
601
|
-
fields: ["id"]
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
);
|
|
608
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
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) {
|
|
609
962
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
610
963
|
}
|
|
611
|
-
if (
|
|
964
|
+
if (lockedRelease.releasedAt) {
|
|
612
965
|
throw new errors.ValidationError("Release already published");
|
|
613
966
|
}
|
|
614
|
-
if (
|
|
615
|
-
throw new errors.ValidationError("
|
|
967
|
+
if (lockedRelease.status === "failed") {
|
|
968
|
+
throw new errors.ValidationError("Release failed to publish");
|
|
616
969
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
entriesToUnpublishIds: []
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
if (action.type === "publish") {
|
|
629
|
-
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
630
|
-
} else {
|
|
631
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
632
|
-
}
|
|
633
|
-
} else {
|
|
634
|
-
singleTypeActions.push({
|
|
635
|
-
uid: contentTypeUid,
|
|
636
|
-
action: action.type,
|
|
637
|
-
id: action.entry.id
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
642
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
643
|
-
await strapi2.db.transaction(async () => {
|
|
644
|
-
for (const { uid, action, id } of singleTypeActions) {
|
|
645
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
646
|
-
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
647
|
-
try {
|
|
648
|
-
if (action === "publish") {
|
|
649
|
-
await entityManagerService.publish(entry, uid);
|
|
650
|
-
} else {
|
|
651
|
-
await entityManagerService.unpublish(entry, uid);
|
|
652
|
-
}
|
|
653
|
-
} catch (error) {
|
|
654
|
-
if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
655
|
-
} else {
|
|
656
|
-
throw error;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
661
|
-
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
662
|
-
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
663
|
-
const entriesToPublish = await strapi2.entityService.findMany(
|
|
664
|
-
contentTypeUid,
|
|
665
|
-
{
|
|
666
|
-
filters: {
|
|
667
|
-
id: {
|
|
668
|
-
$in: entriestoPublishIds
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
populate
|
|
672
|
-
}
|
|
673
|
-
);
|
|
674
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
675
|
-
contentTypeUid,
|
|
676
|
-
{
|
|
677
|
-
filters: {
|
|
678
|
-
id: {
|
|
679
|
-
$in: entriesToUnpublishIds
|
|
680
|
-
}
|
|
681
|
-
},
|
|
682
|
-
populate
|
|
683
|
-
}
|
|
684
|
-
);
|
|
685
|
-
if (entriesToPublish.length > 0) {
|
|
686
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
970
|
+
try {
|
|
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);
|
|
687
978
|
}
|
|
688
|
-
|
|
689
|
-
|
|
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
|
+
);
|
|
690
986
|
}
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
releasedAt: /* @__PURE__ */ new Date()
|
|
700
|
-
},
|
|
701
|
-
populate: {
|
|
702
|
-
actions: {
|
|
703
|
-
// @ts-expect-error is not expecting count but it is working
|
|
704
|
-
count: true
|
|
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()
|
|
705
995
|
}
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
996
|
+
});
|
|
709
997
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
710
998
|
isPublished: true,
|
|
711
|
-
release:
|
|
999
|
+
release: release22
|
|
712
1000
|
});
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
} catch (error) {
|
|
717
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
1001
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
1002
|
+
return { release: release22, error: null };
|
|
1003
|
+
} catch (error2) {
|
|
718
1004
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
719
1005
|
isPublished: false,
|
|
720
|
-
error
|
|
1006
|
+
error: error2
|
|
721
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
|
+
};
|
|
722
1015
|
}
|
|
1016
|
+
});
|
|
1017
|
+
if (error) {
|
|
723
1018
|
throw error;
|
|
724
1019
|
}
|
|
1020
|
+
return release2;
|
|
725
1021
|
},
|
|
726
1022
|
async updateAction(actionId, releaseId, update) {
|
|
727
1023
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
@@ -760,10 +1056,60 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
760
1056
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
761
1057
|
);
|
|
762
1058
|
}
|
|
1059
|
+
this.updateReleaseStatus(releaseId);
|
|
763
1060
|
return deletedAction;
|
|
1061
|
+
},
|
|
1062
|
+
async updateReleaseStatus(releaseId) {
|
|
1063
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
1064
|
+
this.countActions({
|
|
1065
|
+
filters: {
|
|
1066
|
+
release: releaseId
|
|
1067
|
+
}
|
|
1068
|
+
}),
|
|
1069
|
+
this.countActions({
|
|
1070
|
+
filters: {
|
|
1071
|
+
release: releaseId,
|
|
1072
|
+
isEntryValid: false
|
|
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
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1088
|
+
where: {
|
|
1089
|
+
id: releaseId
|
|
1090
|
+
},
|
|
1091
|
+
data: {
|
|
1092
|
+
status: "ready"
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1097
|
+
where: {
|
|
1098
|
+
id: releaseId
|
|
1099
|
+
},
|
|
1100
|
+
data: {
|
|
1101
|
+
status: "empty"
|
|
1102
|
+
}
|
|
1103
|
+
});
|
|
764
1104
|
}
|
|
765
1105
|
};
|
|
766
1106
|
};
|
|
1107
|
+
class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
1108
|
+
constructor(message) {
|
|
1109
|
+
super(message);
|
|
1110
|
+
this.name = "AlreadyOnReleaseError";
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
767
1113
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
768
1114
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
769
1115
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -776,7 +1122,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
776
1122
|
(action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
|
|
777
1123
|
);
|
|
778
1124
|
if (isEntryInRelease) {
|
|
779
|
-
throw new
|
|
1125
|
+
throw new AlreadyOnReleaseError(
|
|
780
1126
|
`Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
|
|
781
1127
|
);
|
|
782
1128
|
}
|
|
@@ -884,7 +1230,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
884
1230
|
const services = {
|
|
885
1231
|
release: createReleaseService,
|
|
886
1232
|
"release-validation": createReleaseValidationService,
|
|
887
|
-
|
|
1233
|
+
scheduling: createSchedulingService
|
|
888
1234
|
};
|
|
889
1235
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
890
1236
|
name: yup.string().trim().required(),
|
|
@@ -937,7 +1283,12 @@ const releaseController = {
|
|
|
937
1283
|
}
|
|
938
1284
|
};
|
|
939
1285
|
});
|
|
940
|
-
|
|
1286
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1287
|
+
where: {
|
|
1288
|
+
releasedAt: null
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
941
1292
|
}
|
|
942
1293
|
},
|
|
943
1294
|
async findOne(ctx) {
|
|
@@ -966,6 +1317,33 @@ const releaseController = {
|
|
|
966
1317
|
};
|
|
967
1318
|
ctx.body = { data };
|
|
968
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
|
+
},
|
|
969
1347
|
async create(ctx) {
|
|
970
1348
|
const user = ctx.state.user;
|
|
971
1349
|
const releaseArgs = ctx.request.body;
|
|
@@ -1055,6 +1433,38 @@ const releaseActionController = {
|
|
|
1055
1433
|
data: releaseAction2
|
|
1056
1434
|
};
|
|
1057
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
|
+
},
|
|
1058
1468
|
async findMany(ctx) {
|
|
1059
1469
|
const releaseId = ctx.params.releaseId;
|
|
1060
1470
|
const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
@@ -1123,6 +1533,22 @@ const controllers = { release: releaseController, "release-action": releaseActio
|
|
|
1123
1533
|
const release = {
|
|
1124
1534
|
type: "admin",
|
|
1125
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
|
+
},
|
|
1126
1552
|
{
|
|
1127
1553
|
method: "POST",
|
|
1128
1554
|
path: "/",
|
|
@@ -1240,6 +1666,22 @@ const releaseAction = {
|
|
|
1240
1666
|
]
|
|
1241
1667
|
}
|
|
1242
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
|
+
},
|
|
1243
1685
|
{
|
|
1244
1686
|
method: "GET",
|
|
1245
1687
|
path: "/:releaseId/actions",
|
|
@@ -1308,6 +1750,9 @@ const getPlugin = () => {
|
|
|
1308
1750
|
};
|
|
1309
1751
|
}
|
|
1310
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
|
|
1311
1756
|
contentTypes
|
|
1312
1757
|
};
|
|
1313
1758
|
};
|