@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.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const utils = require("@strapi/utils");
|
|
3
|
+
const isEqual = require("lodash/isEqual");
|
|
3
4
|
const lodash = require("lodash");
|
|
4
5
|
const _ = require("lodash/fp");
|
|
5
6
|
const EE = require("@strapi/strapi/dist/utils/ee");
|
|
@@ -24,6 +25,7 @@ function _interopNamespace(e) {
|
|
|
24
25
|
n.default = e;
|
|
25
26
|
return Object.freeze(n);
|
|
26
27
|
}
|
|
28
|
+
const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
|
|
27
29
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
28
30
|
const EE__default = /* @__PURE__ */ _interopDefault(EE);
|
|
29
31
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
@@ -76,6 +78,29 @@ const ACTIONS = [
|
|
|
76
78
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
77
79
|
RELEASES_PUBLISH: "releases.publish"
|
|
78
80
|
};
|
|
81
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
82
|
+
return strapi2.plugin("content-releases").service(name);
|
|
83
|
+
};
|
|
84
|
+
const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
85
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
86
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
87
|
+
const entry = await strapi2.entityService.findOne(contentTypeUid, entryId, { populate });
|
|
88
|
+
return entry;
|
|
89
|
+
};
|
|
90
|
+
const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
91
|
+
try {
|
|
92
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
93
|
+
strapi2.getModel(contentTypeUid),
|
|
94
|
+
entry,
|
|
95
|
+
void 0,
|
|
96
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
97
|
+
entry
|
|
98
|
+
);
|
|
99
|
+
return true;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
79
104
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
80
105
|
oldContentTypes,
|
|
81
106
|
contentTypes: contentTypes2
|
|
@@ -102,31 +127,202 @@ async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes:
|
|
|
102
127
|
});
|
|
103
128
|
}
|
|
104
129
|
}
|
|
130
|
+
async function migrateIsValidAndStatusReleases() {
|
|
131
|
+
const releasesWithoutStatus = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
132
|
+
where: {
|
|
133
|
+
status: null,
|
|
134
|
+
releasedAt: null
|
|
135
|
+
},
|
|
136
|
+
populate: {
|
|
137
|
+
actions: {
|
|
138
|
+
populate: {
|
|
139
|
+
entry: true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
utils.mapAsync(releasesWithoutStatus, async (release2) => {
|
|
145
|
+
const actions = release2.actions;
|
|
146
|
+
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
147
|
+
for (const action of notValidatedActions) {
|
|
148
|
+
if (action.entry) {
|
|
149
|
+
const populatedEntry = await getPopulatedEntry(action.contentType, action.entry.id, {
|
|
150
|
+
strapi
|
|
151
|
+
});
|
|
152
|
+
if (populatedEntry) {
|
|
153
|
+
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
154
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
155
|
+
where: {
|
|
156
|
+
id: action.id
|
|
157
|
+
},
|
|
158
|
+
data: {
|
|
159
|
+
isEntryValid
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
166
|
+
});
|
|
167
|
+
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
168
|
+
where: {
|
|
169
|
+
status: null,
|
|
170
|
+
releasedAt: {
|
|
171
|
+
$notNull: true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
utils.mapAsync(publishedReleases, async (release2) => {
|
|
176
|
+
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
177
|
+
where: {
|
|
178
|
+
id: release2.id
|
|
179
|
+
},
|
|
180
|
+
data: {
|
|
181
|
+
status: "done"
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
187
|
+
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
188
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
|
|
189
|
+
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
190
|
+
);
|
|
191
|
+
const releasesAffected = /* @__PURE__ */ new Set();
|
|
192
|
+
utils.mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
193
|
+
const oldContentType = oldContentTypes[contentTypeUID];
|
|
194
|
+
const contentType = contentTypes2[contentTypeUID];
|
|
195
|
+
if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
|
|
196
|
+
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
197
|
+
where: {
|
|
198
|
+
contentType: contentTypeUID
|
|
199
|
+
},
|
|
200
|
+
populate: {
|
|
201
|
+
entry: true,
|
|
202
|
+
release: true
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
await utils.mapAsync(actions, async (action) => {
|
|
206
|
+
if (action.entry && action.release) {
|
|
207
|
+
const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
|
|
208
|
+
strapi
|
|
209
|
+
});
|
|
210
|
+
if (populatedEntry) {
|
|
211
|
+
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
212
|
+
strapi
|
|
213
|
+
});
|
|
214
|
+
releasesAffected.add(action.release.id);
|
|
215
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
216
|
+
where: {
|
|
217
|
+
id: action.id
|
|
218
|
+
},
|
|
219
|
+
data: {
|
|
220
|
+
isEntryValid
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}).then(() => {
|
|
228
|
+
utils.mapAsync(releasesAffected, async (releaseId) => {
|
|
229
|
+
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function disableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
235
|
+
if (!oldContentTypes) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
239
|
+
if (!i18nPlugin) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
for (const uid in contentTypes2) {
|
|
243
|
+
if (!oldContentTypes[uid]) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const oldContentType = oldContentTypes[uid];
|
|
247
|
+
const contentType = contentTypes2[uid];
|
|
248
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
249
|
+
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
250
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
251
|
+
locale: null
|
|
252
|
+
}).where({ contentType: uid }).execute();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async function enableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
257
|
+
if (!oldContentTypes) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
261
|
+
if (!i18nPlugin) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
for (const uid in contentTypes2) {
|
|
265
|
+
if (!oldContentTypes[uid]) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const oldContentType = oldContentTypes[uid];
|
|
269
|
+
const contentType = contentTypes2[uid];
|
|
270
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
271
|
+
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
272
|
+
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
273
|
+
const defaultLocale = await getDefaultLocale();
|
|
274
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
275
|
+
locale: defaultLocale
|
|
276
|
+
}).where({ contentType: uid }).execute();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
105
280
|
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
106
281
|
const register = async ({ strapi: strapi2 }) => {
|
|
107
282
|
if (features$2.isEnabled("cms-content-releases")) {
|
|
108
283
|
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
109
|
-
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
|
|
110
|
-
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
|
|
284
|
+
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish).register(disableContentTypeLocalized);
|
|
285
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
286
|
+
}
|
|
287
|
+
if (strapi2.plugin("graphql")) {
|
|
288
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
289
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
290
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
111
291
|
}
|
|
112
|
-
};
|
|
113
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
114
|
-
return strapi2.plugin("content-releases").service(name);
|
|
115
292
|
};
|
|
116
293
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
117
294
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
118
295
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
296
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
297
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
298
|
+
);
|
|
119
299
|
strapi2.db.lifecycles.subscribe({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
300
|
+
models: contentTypesWithDraftAndPublish,
|
|
301
|
+
async afterDelete(event) {
|
|
302
|
+
try {
|
|
303
|
+
const { model, result } = event;
|
|
304
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
305
|
+
const { id } = result;
|
|
306
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
307
|
+
where: {
|
|
308
|
+
actions: {
|
|
309
|
+
target_type: model.uid,
|
|
310
|
+
target_id: id
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
315
|
+
where: {
|
|
316
|
+
target_type: model.uid,
|
|
317
|
+
target_id: id
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
for (const release2 of releases) {
|
|
321
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
128
322
|
}
|
|
129
|
-
}
|
|
323
|
+
}
|
|
324
|
+
} catch (error) {
|
|
325
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
130
326
|
}
|
|
131
327
|
},
|
|
132
328
|
/**
|
|
@@ -146,41 +342,94 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
146
342
|
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
147
343
|
*/
|
|
148
344
|
async afterDeleteMany(event) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
345
|
+
try {
|
|
346
|
+
const { model, state } = event;
|
|
347
|
+
const entriesToDelete = state.entriesToDelete;
|
|
348
|
+
if (entriesToDelete) {
|
|
349
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
350
|
+
where: {
|
|
351
|
+
actions: {
|
|
352
|
+
target_type: model.uid,
|
|
353
|
+
target_id: {
|
|
354
|
+
$in: entriesToDelete.map(
|
|
355
|
+
(entry) => entry.id
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
157
359
|
}
|
|
360
|
+
});
|
|
361
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
362
|
+
where: {
|
|
363
|
+
target_type: model.uid,
|
|
364
|
+
target_id: {
|
|
365
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
for (const release2 of releases) {
|
|
370
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
158
371
|
}
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
375
|
+
error
|
|
159
376
|
});
|
|
160
377
|
}
|
|
378
|
+
},
|
|
379
|
+
async afterUpdate(event) {
|
|
380
|
+
try {
|
|
381
|
+
const { model, result } = event;
|
|
382
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
383
|
+
const isEntryValid = await getEntryValidStatus(
|
|
384
|
+
model.uid,
|
|
385
|
+
result,
|
|
386
|
+
{
|
|
387
|
+
strapi: strapi2
|
|
388
|
+
}
|
|
389
|
+
);
|
|
390
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
391
|
+
where: {
|
|
392
|
+
target_type: model.uid,
|
|
393
|
+
target_id: result.id
|
|
394
|
+
},
|
|
395
|
+
data: {
|
|
396
|
+
isEntryValid
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
400
|
+
where: {
|
|
401
|
+
actions: {
|
|
402
|
+
target_type: model.uid,
|
|
403
|
+
target_id: result.id
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
for (const release2 of releases) {
|
|
408
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
} catch (error) {
|
|
412
|
+
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
413
|
+
}
|
|
161
414
|
}
|
|
162
415
|
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
}
|
|
416
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
417
|
+
strapi2.log.error(
|
|
418
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
419
|
+
);
|
|
420
|
+
throw err;
|
|
421
|
+
});
|
|
422
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
423
|
+
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
424
|
+
});
|
|
174
425
|
}
|
|
175
426
|
};
|
|
176
427
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
job.cancel();
|
|
183
|
-
}
|
|
428
|
+
const scheduledJobs = getService("scheduling", {
|
|
429
|
+
strapi: strapi2
|
|
430
|
+
}).getAll();
|
|
431
|
+
for (const [, job] of scheduledJobs) {
|
|
432
|
+
job.cancel();
|
|
184
433
|
}
|
|
185
434
|
};
|
|
186
435
|
const schema$1 = {
|
|
@@ -215,6 +464,11 @@ const schema$1 = {
|
|
|
215
464
|
timezone: {
|
|
216
465
|
type: "string"
|
|
217
466
|
},
|
|
467
|
+
status: {
|
|
468
|
+
type: "enumeration",
|
|
469
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
470
|
+
required: true
|
|
471
|
+
},
|
|
218
472
|
actions: {
|
|
219
473
|
type: "relation",
|
|
220
474
|
relation: "oneToMany",
|
|
@@ -267,6 +521,9 @@ const schema = {
|
|
|
267
521
|
relation: "manyToOne",
|
|
268
522
|
target: RELEASE_MODEL_UID,
|
|
269
523
|
inversedBy: "actions"
|
|
524
|
+
},
|
|
525
|
+
isEntryValid: {
|
|
526
|
+
type: "boolean"
|
|
270
527
|
}
|
|
271
528
|
}
|
|
272
529
|
};
|
|
@@ -297,6 +554,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
297
554
|
release: release2
|
|
298
555
|
});
|
|
299
556
|
};
|
|
557
|
+
const publishSingleTypeAction = async (uid, actionType, entryId) => {
|
|
558
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
559
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
560
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
561
|
+
const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
|
|
562
|
+
try {
|
|
563
|
+
if (actionType === "publish") {
|
|
564
|
+
await entityManagerService.publish(entry, uid);
|
|
565
|
+
} else {
|
|
566
|
+
await entityManagerService.unpublish(entry, uid);
|
|
567
|
+
}
|
|
568
|
+
} catch (error) {
|
|
569
|
+
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
570
|
+
;
|
|
571
|
+
else {
|
|
572
|
+
throw error;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
|
|
577
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
578
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
579
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
580
|
+
const entriesToPublish = await strapi2.entityService.findMany(uid, {
|
|
581
|
+
filters: {
|
|
582
|
+
id: {
|
|
583
|
+
$in: entriesToPublishIds
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
populate
|
|
587
|
+
});
|
|
588
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
|
|
589
|
+
filters: {
|
|
590
|
+
id: {
|
|
591
|
+
$in: entriestoUnpublishIds
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
populate
|
|
595
|
+
});
|
|
596
|
+
if (entriesToPublish.length > 0) {
|
|
597
|
+
await entityManagerService.publishMany(entriesToPublish, uid);
|
|
598
|
+
}
|
|
599
|
+
if (entriesToUnpublish.length > 0) {
|
|
600
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, uid);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
const getFormattedActions = async (releaseId) => {
|
|
604
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
605
|
+
where: {
|
|
606
|
+
release: {
|
|
607
|
+
id: releaseId
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
populate: {
|
|
611
|
+
entry: {
|
|
612
|
+
fields: ["id"]
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
if (actions.length === 0) {
|
|
617
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
618
|
+
}
|
|
619
|
+
const collectionTypeActions = {};
|
|
620
|
+
const singleTypeActions = [];
|
|
621
|
+
for (const action of actions) {
|
|
622
|
+
const contentTypeUid = action.contentType;
|
|
623
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
624
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
625
|
+
collectionTypeActions[contentTypeUid] = {
|
|
626
|
+
entriesToPublishIds: [],
|
|
627
|
+
entriesToUnpublishIds: []
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
if (action.type === "publish") {
|
|
631
|
+
collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
|
|
632
|
+
} else {
|
|
633
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
634
|
+
}
|
|
635
|
+
} else {
|
|
636
|
+
singleTypeActions.push({
|
|
637
|
+
uid: contentTypeUid,
|
|
638
|
+
action: action.type,
|
|
639
|
+
id: action.entry.id
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return { collectionTypeActions, singleTypeActions };
|
|
644
|
+
};
|
|
300
645
|
return {
|
|
301
646
|
async create(releaseData, { user }) {
|
|
302
647
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
@@ -311,9 +656,12 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
311
656
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
312
657
|
]);
|
|
313
658
|
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
314
|
-
data:
|
|
659
|
+
data: {
|
|
660
|
+
...releaseWithCreatorFields,
|
|
661
|
+
status: "empty"
|
|
662
|
+
}
|
|
315
663
|
});
|
|
316
|
-
if (
|
|
664
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
317
665
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
318
666
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
319
667
|
}
|
|
@@ -337,12 +685,18 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
337
685
|
}
|
|
338
686
|
});
|
|
339
687
|
},
|
|
340
|
-
async findManyWithContentTypeEntryAttached(contentTypeUid,
|
|
688
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
|
|
689
|
+
let entries = entriesIds;
|
|
690
|
+
if (!Array.isArray(entriesIds)) {
|
|
691
|
+
entries = [entriesIds];
|
|
692
|
+
}
|
|
341
693
|
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
342
694
|
where: {
|
|
343
695
|
actions: {
|
|
344
696
|
target_type: contentTypeUid,
|
|
345
|
-
target_id:
|
|
697
|
+
target_id: {
|
|
698
|
+
$in: entries
|
|
699
|
+
}
|
|
346
700
|
},
|
|
347
701
|
releasedAt: {
|
|
348
702
|
$null: true
|
|
@@ -353,18 +707,25 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
353
707
|
actions: {
|
|
354
708
|
where: {
|
|
355
709
|
target_type: contentTypeUid,
|
|
356
|
-
target_id:
|
|
710
|
+
target_id: {
|
|
711
|
+
$in: entries
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
populate: {
|
|
715
|
+
entry: {
|
|
716
|
+
select: ["id"]
|
|
717
|
+
}
|
|
357
718
|
}
|
|
358
719
|
}
|
|
359
720
|
}
|
|
360
721
|
});
|
|
361
722
|
return releases.map((release2) => {
|
|
362
723
|
if (release2.actions?.length) {
|
|
363
|
-
const
|
|
724
|
+
const actionsForEntry = release2.actions;
|
|
364
725
|
delete release2.actions;
|
|
365
726
|
return {
|
|
366
727
|
...release2,
|
|
367
|
-
|
|
728
|
+
actions: actionsForEntry
|
|
368
729
|
};
|
|
369
730
|
}
|
|
370
731
|
return release2;
|
|
@@ -438,14 +799,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
438
799
|
// @ts-expect-error see above
|
|
439
800
|
data: releaseWithCreatorFields
|
|
440
801
|
});
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
schedulingService.cancel(id);
|
|
447
|
-
}
|
|
802
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
803
|
+
if (releaseData.scheduledAt) {
|
|
804
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
805
|
+
} else if (release2.scheduledAt) {
|
|
806
|
+
schedulingService.cancel(id);
|
|
448
807
|
}
|
|
808
|
+
this.updateReleaseStatus(id);
|
|
449
809
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
450
810
|
return updatedRelease;
|
|
451
811
|
},
|
|
@@ -465,11 +825,14 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
465
825
|
throw new utils.errors.ValidationError("Release already published");
|
|
466
826
|
}
|
|
467
827
|
const { entry, type } = action;
|
|
468
|
-
|
|
828
|
+
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
829
|
+
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
830
|
+
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
469
831
|
data: {
|
|
470
832
|
type,
|
|
471
833
|
contentType: entry.contentType,
|
|
472
834
|
locale: entry.locale,
|
|
835
|
+
isEntryValid,
|
|
473
836
|
entry: {
|
|
474
837
|
id: entry.id,
|
|
475
838
|
__type: entry.contentType,
|
|
@@ -479,6 +842,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
479
842
|
},
|
|
480
843
|
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
481
844
|
});
|
|
845
|
+
this.updateReleaseStatus(releaseId);
|
|
846
|
+
return releaseAction2;
|
|
482
847
|
},
|
|
483
848
|
async findActions(releaseId, query) {
|
|
484
849
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -604,7 +969,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
604
969
|
});
|
|
605
970
|
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
606
971
|
});
|
|
607
|
-
if (
|
|
972
|
+
if (release2.scheduledAt) {
|
|
608
973
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
609
974
|
await schedulingService.cancel(release2.id);
|
|
610
975
|
}
|
|
@@ -612,139 +977,71 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
612
977
|
return release2;
|
|
613
978
|
},
|
|
614
979
|
async publish(releaseId) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
actions: {
|
|
622
|
-
populate: {
|
|
623
|
-
entry: {
|
|
624
|
-
fields: ["id"]
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
);
|
|
631
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
980
|
+
const {
|
|
981
|
+
release: release2,
|
|
982
|
+
error
|
|
983
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
984
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
985
|
+
if (!lockedRelease) {
|
|
632
986
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
633
987
|
}
|
|
634
|
-
if (
|
|
988
|
+
if (lockedRelease.releasedAt) {
|
|
635
989
|
throw new utils.errors.ValidationError("Release already published");
|
|
636
990
|
}
|
|
637
|
-
if (
|
|
638
|
-
throw new utils.errors.ValidationError("
|
|
991
|
+
if (lockedRelease.status === "failed") {
|
|
992
|
+
throw new utils.errors.ValidationError("Release failed to publish");
|
|
639
993
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
entriesToUnpublishIds: []
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
if (action.type === "publish") {
|
|
652
|
-
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
653
|
-
} else {
|
|
654
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
655
|
-
}
|
|
656
|
-
} else {
|
|
657
|
-
singleTypeActions.push({
|
|
658
|
-
uid: contentTypeUid,
|
|
659
|
-
action: action.type,
|
|
660
|
-
id: action.entry.id
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
665
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
666
|
-
await strapi2.db.transaction(async () => {
|
|
667
|
-
for (const { uid, action, id } of singleTypeActions) {
|
|
668
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
669
|
-
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
670
|
-
try {
|
|
671
|
-
if (action === "publish") {
|
|
672
|
-
await entityManagerService.publish(entry, uid);
|
|
673
|
-
} else {
|
|
674
|
-
await entityManagerService.unpublish(entry, uid);
|
|
675
|
-
}
|
|
676
|
-
} catch (error) {
|
|
677
|
-
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
678
|
-
} else {
|
|
679
|
-
throw error;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
684
|
-
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
685
|
-
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
686
|
-
const entriesToPublish = await strapi2.entityService.findMany(
|
|
687
|
-
contentTypeUid,
|
|
688
|
-
{
|
|
689
|
-
filters: {
|
|
690
|
-
id: {
|
|
691
|
-
$in: entriestoPublishIds
|
|
692
|
-
}
|
|
693
|
-
},
|
|
694
|
-
populate
|
|
695
|
-
}
|
|
696
|
-
);
|
|
697
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
698
|
-
contentTypeUid,
|
|
699
|
-
{
|
|
700
|
-
filters: {
|
|
701
|
-
id: {
|
|
702
|
-
$in: entriesToUnpublishIds
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
|
-
populate
|
|
706
|
-
}
|
|
707
|
-
);
|
|
708
|
-
if (entriesToPublish.length > 0) {
|
|
709
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
994
|
+
try {
|
|
995
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
996
|
+
const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
|
|
997
|
+
releaseId
|
|
998
|
+
);
|
|
999
|
+
await strapi2.db.transaction(async () => {
|
|
1000
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
1001
|
+
await publishSingleTypeAction(uid, action, id);
|
|
710
1002
|
}
|
|
711
|
-
|
|
712
|
-
|
|
1003
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
1004
|
+
const uid = contentTypeUid;
|
|
1005
|
+
await publishCollectionTypeAction(
|
|
1006
|
+
uid,
|
|
1007
|
+
collectionTypeActions[uid].entriesToPublishIds,
|
|
1008
|
+
collectionTypeActions[uid].entriesToUnpublishIds
|
|
1009
|
+
);
|
|
713
1010
|
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
releasedAt: /* @__PURE__ */ new Date()
|
|
723
|
-
},
|
|
724
|
-
populate: {
|
|
725
|
-
actions: {
|
|
726
|
-
// @ts-expect-error is not expecting count but it is working
|
|
727
|
-
count: true
|
|
1011
|
+
});
|
|
1012
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1013
|
+
where: {
|
|
1014
|
+
id: releaseId
|
|
1015
|
+
},
|
|
1016
|
+
data: {
|
|
1017
|
+
status: "done",
|
|
1018
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
728
1019
|
}
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
1020
|
+
});
|
|
732
1021
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
733
1022
|
isPublished: true,
|
|
734
|
-
release:
|
|
1023
|
+
release: release22
|
|
735
1024
|
});
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
} catch (error) {
|
|
740
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
1025
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
1026
|
+
return { release: release22, error: null };
|
|
1027
|
+
} catch (error2) {
|
|
741
1028
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
742
1029
|
isPublished: false,
|
|
743
|
-
error
|
|
1030
|
+
error: error2
|
|
744
1031
|
});
|
|
1032
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
1033
|
+
status: "failed"
|
|
1034
|
+
}).transacting(trx).execute();
|
|
1035
|
+
return {
|
|
1036
|
+
release: null,
|
|
1037
|
+
error: error2
|
|
1038
|
+
};
|
|
745
1039
|
}
|
|
1040
|
+
});
|
|
1041
|
+
if (error) {
|
|
746
1042
|
throw error;
|
|
747
1043
|
}
|
|
1044
|
+
return release2;
|
|
748
1045
|
},
|
|
749
1046
|
async updateAction(actionId, releaseId, update) {
|
|
750
1047
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
@@ -783,10 +1080,60 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
783
1080
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
784
1081
|
);
|
|
785
1082
|
}
|
|
1083
|
+
this.updateReleaseStatus(releaseId);
|
|
786
1084
|
return deletedAction;
|
|
1085
|
+
},
|
|
1086
|
+
async updateReleaseStatus(releaseId) {
|
|
1087
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
1088
|
+
this.countActions({
|
|
1089
|
+
filters: {
|
|
1090
|
+
release: releaseId
|
|
1091
|
+
}
|
|
1092
|
+
}),
|
|
1093
|
+
this.countActions({
|
|
1094
|
+
filters: {
|
|
1095
|
+
release: releaseId,
|
|
1096
|
+
isEntryValid: false
|
|
1097
|
+
}
|
|
1098
|
+
})
|
|
1099
|
+
]);
|
|
1100
|
+
if (totalActions > 0) {
|
|
1101
|
+
if (invalidActions > 0) {
|
|
1102
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1103
|
+
where: {
|
|
1104
|
+
id: releaseId
|
|
1105
|
+
},
|
|
1106
|
+
data: {
|
|
1107
|
+
status: "blocked"
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1112
|
+
where: {
|
|
1113
|
+
id: releaseId
|
|
1114
|
+
},
|
|
1115
|
+
data: {
|
|
1116
|
+
status: "ready"
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1121
|
+
where: {
|
|
1122
|
+
id: releaseId
|
|
1123
|
+
},
|
|
1124
|
+
data: {
|
|
1125
|
+
status: "empty"
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
787
1128
|
}
|
|
788
1129
|
};
|
|
789
1130
|
};
|
|
1131
|
+
class AlreadyOnReleaseError extends utils.errors.ApplicationError {
|
|
1132
|
+
constructor(message) {
|
|
1133
|
+
super(message);
|
|
1134
|
+
this.name = "AlreadyOnReleaseError";
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
790
1137
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
791
1138
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
792
1139
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -799,7 +1146,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
799
1146
|
(action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
|
|
800
1147
|
);
|
|
801
1148
|
if (isEntryInRelease) {
|
|
802
|
-
throw new
|
|
1149
|
+
throw new AlreadyOnReleaseError(
|
|
803
1150
|
`Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
|
|
804
1151
|
);
|
|
805
1152
|
}
|
|
@@ -907,7 +1254,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
907
1254
|
const services = {
|
|
908
1255
|
release: createReleaseService,
|
|
909
1256
|
"release-validation": createReleaseValidationService,
|
|
910
|
-
|
|
1257
|
+
scheduling: createSchedulingService
|
|
911
1258
|
};
|
|
912
1259
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
913
1260
|
name: yup__namespace.string().trim().required(),
|
|
@@ -960,7 +1307,12 @@ const releaseController = {
|
|
|
960
1307
|
}
|
|
961
1308
|
};
|
|
962
1309
|
});
|
|
963
|
-
|
|
1310
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1311
|
+
where: {
|
|
1312
|
+
releasedAt: null
|
|
1313
|
+
}
|
|
1314
|
+
});
|
|
1315
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
964
1316
|
}
|
|
965
1317
|
},
|
|
966
1318
|
async findOne(ctx) {
|
|
@@ -989,6 +1341,33 @@ const releaseController = {
|
|
|
989
1341
|
};
|
|
990
1342
|
ctx.body = { data };
|
|
991
1343
|
},
|
|
1344
|
+
async mapEntriesToReleases(ctx) {
|
|
1345
|
+
const { contentTypeUid, entriesIds } = ctx.query;
|
|
1346
|
+
if (!contentTypeUid || !entriesIds) {
|
|
1347
|
+
throw new utils.errors.ValidationError("Missing required query parameters");
|
|
1348
|
+
}
|
|
1349
|
+
const releaseService = getService("release", { strapi });
|
|
1350
|
+
const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
|
|
1351
|
+
contentTypeUid,
|
|
1352
|
+
entriesIds
|
|
1353
|
+
);
|
|
1354
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1355
|
+
(acc, release2) => {
|
|
1356
|
+
release2.actions.forEach((action) => {
|
|
1357
|
+
if (!acc[action.entry.id]) {
|
|
1358
|
+
acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
|
|
1359
|
+
} else {
|
|
1360
|
+
acc[action.entry.id].push({ id: release2.id, name: release2.name });
|
|
1361
|
+
}
|
|
1362
|
+
});
|
|
1363
|
+
return acc;
|
|
1364
|
+
},
|
|
1365
|
+
{}
|
|
1366
|
+
);
|
|
1367
|
+
ctx.body = {
|
|
1368
|
+
data: mappedEntriesInReleases
|
|
1369
|
+
};
|
|
1370
|
+
},
|
|
992
1371
|
async create(ctx) {
|
|
993
1372
|
const user = ctx.state.user;
|
|
994
1373
|
const releaseArgs = ctx.request.body;
|
|
@@ -1078,6 +1457,38 @@ const releaseActionController = {
|
|
|
1078
1457
|
data: releaseAction2
|
|
1079
1458
|
};
|
|
1080
1459
|
},
|
|
1460
|
+
async createMany(ctx) {
|
|
1461
|
+
const releaseId = ctx.params.releaseId;
|
|
1462
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1463
|
+
await Promise.all(
|
|
1464
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1465
|
+
);
|
|
1466
|
+
const releaseService = getService("release", { strapi });
|
|
1467
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1468
|
+
const releaseActions2 = await Promise.all(
|
|
1469
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1470
|
+
try {
|
|
1471
|
+
const action = await releaseService.createAction(releaseId, releaseActionArgs);
|
|
1472
|
+
return action;
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1475
|
+
return null;
|
|
1476
|
+
}
|
|
1477
|
+
throw error;
|
|
1478
|
+
}
|
|
1479
|
+
})
|
|
1480
|
+
);
|
|
1481
|
+
return releaseActions2;
|
|
1482
|
+
});
|
|
1483
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1484
|
+
ctx.body = {
|
|
1485
|
+
data: newReleaseActions,
|
|
1486
|
+
meta: {
|
|
1487
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1488
|
+
totalEntries: releaseActions.length
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
},
|
|
1081
1492
|
async findMany(ctx) {
|
|
1082
1493
|
const releaseId = ctx.params.releaseId;
|
|
1083
1494
|
const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
@@ -1146,6 +1557,22 @@ const controllers = { release: releaseController, "release-action": releaseActio
|
|
|
1146
1557
|
const release = {
|
|
1147
1558
|
type: "admin",
|
|
1148
1559
|
routes: [
|
|
1560
|
+
{
|
|
1561
|
+
method: "GET",
|
|
1562
|
+
path: "/mapEntriesToReleases",
|
|
1563
|
+
handler: "release.mapEntriesToReleases",
|
|
1564
|
+
config: {
|
|
1565
|
+
policies: [
|
|
1566
|
+
"admin::isAuthenticatedAdmin",
|
|
1567
|
+
{
|
|
1568
|
+
name: "admin::hasPermissions",
|
|
1569
|
+
config: {
|
|
1570
|
+
actions: ["plugin::content-releases.read"]
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
]
|
|
1574
|
+
}
|
|
1575
|
+
},
|
|
1149
1576
|
{
|
|
1150
1577
|
method: "POST",
|
|
1151
1578
|
path: "/",
|
|
@@ -1263,6 +1690,22 @@ const releaseAction = {
|
|
|
1263
1690
|
]
|
|
1264
1691
|
}
|
|
1265
1692
|
},
|
|
1693
|
+
{
|
|
1694
|
+
method: "POST",
|
|
1695
|
+
path: "/:releaseId/actions/bulk",
|
|
1696
|
+
handler: "release-action.createMany",
|
|
1697
|
+
config: {
|
|
1698
|
+
policies: [
|
|
1699
|
+
"admin::isAuthenticatedAdmin",
|
|
1700
|
+
{
|
|
1701
|
+
name: "admin::hasPermissions",
|
|
1702
|
+
config: {
|
|
1703
|
+
actions: ["plugin::content-releases.create-action"]
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
]
|
|
1707
|
+
}
|
|
1708
|
+
},
|
|
1266
1709
|
{
|
|
1267
1710
|
method: "GET",
|
|
1268
1711
|
path: "/:releaseId/actions",
|
|
@@ -1331,6 +1774,9 @@ const getPlugin = () => {
|
|
|
1331
1774
|
};
|
|
1332
1775
|
}
|
|
1333
1776
|
return {
|
|
1777
|
+
// Always return register, it handles its own feature check
|
|
1778
|
+
register,
|
|
1779
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1334
1780
|
contentTypes
|
|
1335
1781
|
};
|
|
1336
1782
|
};
|