@strapi/content-releases 0.0.0-next.39c0188c3aa01bec7b64b948211571d5159e811d → 0.0.0-next.3a1d87a9c3b1d4d89f741c6ecd7f501230fd8d76
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-CiZCkScI.mjs +1558 -0
- package/dist/_chunks/App-CiZCkScI.mjs.map +1 -0
- package/dist/_chunks/App-SGjO5UPV.js +1578 -0
- package/dist/_chunks/App-SGjO5UPV.js.map +1 -0
- package/dist/_chunks/PurchaseContentReleases--qQepXpP.js +52 -0
- package/dist/_chunks/PurchaseContentReleases--qQepXpP.js.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs +52 -0
- package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js +178 -0
- package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs +178 -0
- package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs.map +1 -0
- package/dist/_chunks/en-BWPPsSH-.js +102 -0
- package/dist/{admin/chunks/en-B2EeDoOz.js.map → _chunks/en-BWPPsSH-.js.map} +1 -1
- package/dist/_chunks/en-D9Q4YW03.mjs +102 -0
- package/dist/_chunks/en-D9Q4YW03.mjs.map +1 -0
- package/dist/_chunks/index-BjvFfTtA.mjs +1386 -0
- package/dist/_chunks/index-BjvFfTtA.mjs.map +1 -0
- package/dist/_chunks/index-CyU534vL.js +1404 -0
- package/dist/_chunks/index-CyU534vL.js.map +1 -0
- package/dist/_chunks/schemas-DBYv9gK8.js +61 -0
- package/dist/_chunks/schemas-DBYv9gK8.js.map +1 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs +44 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs.map +1 -0
- package/dist/admin/index.js +3 -18
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +5 -13
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/components/ReleaseListCell.d.ts +1 -1
- package/dist/server/index.js +1869 -2165
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1861 -2156
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/destroy.d.ts +1 -1
- package/dist/server/src/destroy.d.ts.map +1 -1
- package/dist/server/src/middlewares/documents.d.ts +1 -1
- package/dist/server/src/middlewares/documents.d.ts.map +1 -1
- package/dist/server/src/services/scheduling.d.ts +1 -1
- package/dist/server/src/services/scheduling.d.ts.map +1 -1
- package/dist/server/src/services/validation.d.ts +1 -1
- package/dist/server/src/services/validation.d.ts.map +1 -1
- package/dist/shared/contracts/release-actions.d.ts +1 -0
- package/dist/shared/contracts/releases.d.ts +1 -0
- package/dist/shared/contracts/settings.d.ts +2 -1
- package/dist/shared/contracts/settings.d.ts.map +1 -1
- package/dist/shared/types.d.ts +1 -0
- package/package.json +11 -14
- package/dist/admin/chunks/App-B9yCdSLJ.js +0 -1866
- package/dist/admin/chunks/App-B9yCdSLJ.js.map +0 -1
- package/dist/admin/chunks/App-CZiJBsId.js +0 -1845
- package/dist/admin/chunks/App-CZiJBsId.js.map +0 -1
- package/dist/admin/chunks/PurchaseContentReleases-BCME5SQU.js +0 -55
- package/dist/admin/chunks/PurchaseContentReleases-BCME5SQU.js.map +0 -1
- package/dist/admin/chunks/PurchaseContentReleases-S1ccDSwp.js +0 -53
- package/dist/admin/chunks/PurchaseContentReleases-S1ccDSwp.js.map +0 -1
- package/dist/admin/chunks/ReleasesSettingsPage-Csq9aGu9.js +0 -206
- package/dist/admin/chunks/ReleasesSettingsPage-Csq9aGu9.js.map +0 -1
- package/dist/admin/chunks/ReleasesSettingsPage-DFVGppsl.js +0 -208
- package/dist/admin/chunks/ReleasesSettingsPage-DFVGppsl.js.map +0 -1
- package/dist/admin/chunks/en-B2EeDoOz.js +0 -101
- package/dist/admin/chunks/en-BzpFfVeO.js +0 -103
- package/dist/admin/chunks/en-BzpFfVeO.js.map +0 -1
- package/dist/admin/chunks/index-1nn-zHX-.js +0 -1657
- package/dist/admin/chunks/index-1nn-zHX-.js.map +0 -1
- package/dist/admin/chunks/index-BexsXldu.js +0 -1618
- package/dist/admin/chunks/index-BexsXldu.js.map +0 -1
- package/dist/admin/chunks/schemas-DMt8h1z-.js +0 -43
- package/dist/admin/chunks/schemas-DMt8h1z-.js.map +0 -1
- package/dist/admin/chunks/schemas-DS7NeFDN.js +0 -65
- package/dist/admin/chunks/schemas-DS7NeFDN.js.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -1,2332 +1,2037 @@
|
|
|
1
|
-
import { contentTypes as contentTypes$1, async, setCreatorFields, errors, yup as yup$1, validateYupSchema } from
|
|
2
|
-
import isEqual from
|
|
3
|
-
import { difference, keys } from
|
|
4
|
-
import _ from
|
|
5
|
-
import { scheduleJob } from
|
|
6
|
-
import * as yup from
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const RELEASE_ACTION_MODEL_UID = 'plugin::content-releases.release-action';
|
|
1
|
+
import { contentTypes as contentTypes$1, async, setCreatorFields, errors, yup as yup$1, validateYupSchema } from "@strapi/utils";
|
|
2
|
+
import isEqual from "lodash/isEqual";
|
|
3
|
+
import { difference, keys } from "lodash";
|
|
4
|
+
import _ from "lodash/fp";
|
|
5
|
+
import { scheduleJob } from "node-schedule";
|
|
6
|
+
import * as yup from "yup";
|
|
7
|
+
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
8
|
+
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
10
9
|
const ACTIONS = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
10
|
+
{
|
|
11
|
+
section: "plugins",
|
|
12
|
+
displayName: "Read",
|
|
13
|
+
uid: "read",
|
|
14
|
+
pluginName: "content-releases"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
section: "plugins",
|
|
18
|
+
displayName: "Create",
|
|
19
|
+
uid: "create",
|
|
20
|
+
pluginName: "content-releases"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
section: "plugins",
|
|
24
|
+
displayName: "Edit",
|
|
25
|
+
uid: "update",
|
|
26
|
+
pluginName: "content-releases"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
section: "plugins",
|
|
30
|
+
displayName: "Delete",
|
|
31
|
+
uid: "delete",
|
|
32
|
+
pluginName: "content-releases"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
section: "plugins",
|
|
36
|
+
displayName: "Publish",
|
|
37
|
+
uid: "publish",
|
|
38
|
+
pluginName: "content-releases"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
section: "plugins",
|
|
42
|
+
displayName: "Remove an entry from a release",
|
|
43
|
+
uid: "delete-action",
|
|
44
|
+
pluginName: "content-releases"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
section: "plugins",
|
|
48
|
+
displayName: "Add an entry to a release",
|
|
49
|
+
uid: "create-action",
|
|
50
|
+
pluginName: "content-releases"
|
|
51
|
+
},
|
|
52
|
+
// Settings
|
|
53
|
+
{
|
|
54
|
+
uid: "settings.read",
|
|
55
|
+
section: "settings",
|
|
56
|
+
displayName: "Read",
|
|
57
|
+
category: "content releases",
|
|
58
|
+
subCategory: "options",
|
|
59
|
+
pluginName: "content-releases"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
uid: "settings.update",
|
|
63
|
+
section: "settings",
|
|
64
|
+
displayName: "Edit",
|
|
65
|
+
category: "content releases",
|
|
66
|
+
subCategory: "options",
|
|
67
|
+
pluginName: "content-releases"
|
|
68
|
+
}
|
|
70
69
|
];
|
|
71
70
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
72
|
-
|
|
71
|
+
RELEASES_PUBLISH: "releases.publish"
|
|
73
72
|
};
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return strapi1.plugin('content-releases').service(name);
|
|
73
|
+
const getService = (name, { strapi: strapi2 }) => {
|
|
74
|
+
return strapi2.plugin("content-releases").service(name);
|
|
77
75
|
};
|
|
78
|
-
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
contentType,
|
|
84
|
-
documentId,
|
|
85
|
-
locale,
|
|
86
|
-
populate
|
|
87
|
-
}, {
|
|
88
|
-
strapi: strapi1
|
|
89
|
-
});
|
|
90
|
-
return isEntryValid(contentType, entry, {
|
|
91
|
-
strapi: strapi1
|
|
92
|
-
});
|
|
76
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
77
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
78
|
+
const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
|
|
79
|
+
const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
|
|
80
|
+
return isEntryValid(contentType, entry, { strapi: strapi2 });
|
|
93
81
|
};
|
|
94
|
-
const isEntryValid = async (contentTypeUid, entry, { strapi:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
82
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
83
|
+
try {
|
|
84
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
85
|
+
strapi2.getModel(contentTypeUid),
|
|
86
|
+
entry,
|
|
87
|
+
void 0,
|
|
88
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
89
|
+
entry
|
|
90
|
+
);
|
|
91
|
+
const workflowsService = strapi2.plugin("review-workflows").service("workflows");
|
|
92
|
+
const workflow = await workflowsService.getAssignedWorkflow(contentTypeUid, {
|
|
93
|
+
populate: "stageRequiredToPublish"
|
|
94
|
+
});
|
|
95
|
+
if (workflow?.stageRequiredToPublish) {
|
|
96
|
+
return entry.strapi_stage.id === workflow.stageRequiredToPublish.id;
|
|
109
97
|
}
|
|
98
|
+
return true;
|
|
99
|
+
} catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
110
102
|
};
|
|
111
|
-
const getEntry = async ({
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return strapi1.documents(contentType).findOne({
|
|
123
|
-
documentId,
|
|
124
|
-
locale,
|
|
125
|
-
populate,
|
|
126
|
-
status: 'draft'
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
return entry;
|
|
103
|
+
const getEntry = async ({
|
|
104
|
+
contentType,
|
|
105
|
+
documentId,
|
|
106
|
+
locale,
|
|
107
|
+
populate,
|
|
108
|
+
status = "draft"
|
|
109
|
+
}, { strapi: strapi2 }) => {
|
|
110
|
+
if (documentId) {
|
|
111
|
+
const entry = await strapi2.documents(contentType).findOne({ documentId, locale, populate, status });
|
|
112
|
+
if (status === "published" && !entry) {
|
|
113
|
+
return strapi2.documents(contentType).findOne({ documentId, locale, populate, status: "draft" });
|
|
130
114
|
}
|
|
131
|
-
return
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
status
|
|
135
|
-
});
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
return strapi2.documents(contentType).findFirst({ locale, populate, status });
|
|
136
118
|
};
|
|
137
|
-
const getEntryStatus = async (contentType, entry)=>{
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
return 'published';
|
|
119
|
+
const getEntryStatus = async (contentType, entry) => {
|
|
120
|
+
if (entry.publishedAt) {
|
|
121
|
+
return "published";
|
|
122
|
+
}
|
|
123
|
+
const publishedEntry = await strapi.documents(contentType).findOne({
|
|
124
|
+
documentId: entry.documentId,
|
|
125
|
+
locale: entry.locale,
|
|
126
|
+
status: "published",
|
|
127
|
+
fields: ["updatedAt"]
|
|
128
|
+
});
|
|
129
|
+
if (!publishedEntry) {
|
|
130
|
+
return "draft";
|
|
131
|
+
}
|
|
132
|
+
const entryUpdatedAt = new Date(entry.updatedAt).getTime();
|
|
133
|
+
const publishedEntryUpdatedAt = new Date(publishedEntry.updatedAt).getTime();
|
|
134
|
+
if (entryUpdatedAt > publishedEntryUpdatedAt) {
|
|
135
|
+
return "modified";
|
|
136
|
+
}
|
|
137
|
+
return "published";
|
|
158
138
|
};
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
139
|
+
async function deleteActionsOnDisableDraftAndPublish({
|
|
140
|
+
oldContentTypes,
|
|
141
|
+
contentTypes: contentTypes2
|
|
142
|
+
}) {
|
|
143
|
+
if (!oldContentTypes) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
for (const uid in contentTypes2) {
|
|
147
|
+
if (!oldContentTypes[uid]) {
|
|
148
|
+
continue;
|
|
163
149
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const oldContentType = oldContentTypes[uid];
|
|
169
|
-
const contentType = contentTypes[uid];
|
|
170
|
-
if (contentTypes$1.hasDraftAndPublish(oldContentType) && !contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
171
|
-
await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({
|
|
172
|
-
contentType: uid
|
|
173
|
-
}).execute();
|
|
174
|
-
}
|
|
150
|
+
const oldContentType = oldContentTypes[uid];
|
|
151
|
+
const contentType = contentTypes2[uid];
|
|
152
|
+
if (contentTypes$1.hasDraftAndPublish(oldContentType) && !contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
153
|
+
await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
|
|
175
154
|
}
|
|
155
|
+
}
|
|
176
156
|
}
|
|
177
|
-
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes }) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
});
|
|
185
|
-
}
|
|
157
|
+
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
158
|
+
const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes2)) ?? [];
|
|
159
|
+
if (deletedContentTypes.length) {
|
|
160
|
+
await async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
161
|
+
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
162
|
+
});
|
|
163
|
+
}
|
|
186
164
|
}
|
|
187
165
|
async function migrateIsValidAndStatusReleases() {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
166
|
+
const releasesWithoutStatus = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
167
|
+
where: {
|
|
168
|
+
status: null,
|
|
169
|
+
releasedAt: null
|
|
170
|
+
},
|
|
171
|
+
populate: {
|
|
172
|
+
actions: {
|
|
193
173
|
populate: {
|
|
194
|
-
|
|
195
|
-
populate: {
|
|
196
|
-
entry: true
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
async.map(releasesWithoutStatus, async (release)=>{
|
|
202
|
-
const actions = release.actions;
|
|
203
|
-
const notValidatedActions = actions.filter((action)=>action.isEntryValid === null);
|
|
204
|
-
for (const action of notValidatedActions){
|
|
205
|
-
// We need to check the Action is related to a valid entry because we can't assume this is gonna be always the case
|
|
206
|
-
// example: users could make changes directly to their database, or data could be lost
|
|
207
|
-
if (action.entry) {
|
|
208
|
-
const isEntryValid = getDraftEntryValidStatus({
|
|
209
|
-
contentType: action.contentType,
|
|
210
|
-
documentId: action.entryDocumentId,
|
|
211
|
-
locale: action.locale
|
|
212
|
-
}, {
|
|
213
|
-
strapi
|
|
214
|
-
});
|
|
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
|
-
return getService('release', {
|
|
226
|
-
strapi
|
|
227
|
-
}).updateReleaseStatus(release.id);
|
|
228
|
-
});
|
|
229
|
-
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
230
|
-
where: {
|
|
231
|
-
status: null,
|
|
232
|
-
releasedAt: {
|
|
233
|
-
$notNull: true
|
|
234
|
-
}
|
|
174
|
+
entry: true
|
|
235
175
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
async.map(releasesWithoutStatus, async (release2) => {
|
|
180
|
+
const actions = release2.actions;
|
|
181
|
+
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
182
|
+
for (const action of notValidatedActions) {
|
|
183
|
+
if (action.entry) {
|
|
184
|
+
const isEntryValid2 = getDraftEntryValidStatus(
|
|
185
|
+
{
|
|
186
|
+
contentType: action.contentType,
|
|
187
|
+
documentId: action.entryDocumentId,
|
|
188
|
+
locale: action.locale
|
|
189
|
+
},
|
|
190
|
+
{ strapi }
|
|
191
|
+
);
|
|
192
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
193
|
+
where: {
|
|
194
|
+
id: action.id
|
|
195
|
+
},
|
|
196
|
+
data: {
|
|
197
|
+
isEntryValid: isEntryValid2
|
|
198
|
+
}
|
|
245
199
|
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
203
|
+
});
|
|
204
|
+
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
205
|
+
where: {
|
|
206
|
+
status: null,
|
|
207
|
+
releasedAt: {
|
|
208
|
+
$notNull: true
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
async.map(publishedReleases, async (release2) => {
|
|
213
|
+
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
214
|
+
where: {
|
|
215
|
+
id: release2.id
|
|
216
|
+
},
|
|
217
|
+
data: {
|
|
218
|
+
status: "done"
|
|
219
|
+
}
|
|
246
220
|
});
|
|
221
|
+
});
|
|
247
222
|
}
|
|
248
|
-
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes }) {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
}).then(()=>{
|
|
288
|
-
// We need to update the status of the releases affected
|
|
289
|
-
async.map(releasesAffected, async (releaseId)=>{
|
|
290
|
-
return getService('release', {
|
|
291
|
-
strapi
|
|
292
|
-
}).updateReleaseStatus(releaseId);
|
|
223
|
+
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
224
|
+
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
225
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
|
|
226
|
+
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
227
|
+
);
|
|
228
|
+
const releasesAffected = /* @__PURE__ */ new Set();
|
|
229
|
+
async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
230
|
+
const oldContentType = oldContentTypes[contentTypeUID];
|
|
231
|
+
const contentType = contentTypes2[contentTypeUID];
|
|
232
|
+
if (!isEqual(oldContentType?.attributes, contentType?.attributes)) {
|
|
233
|
+
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
234
|
+
where: {
|
|
235
|
+
contentType: contentTypeUID
|
|
236
|
+
},
|
|
237
|
+
populate: {
|
|
238
|
+
entry: true,
|
|
239
|
+
release: true
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
await async.map(actions, async (action) => {
|
|
243
|
+
if (action.entry && action.release && action.type === "publish") {
|
|
244
|
+
const isEntryValid2 = await getDraftEntryValidStatus(
|
|
245
|
+
{
|
|
246
|
+
contentType: contentTypeUID,
|
|
247
|
+
documentId: action.entryDocumentId,
|
|
248
|
+
locale: action.locale
|
|
249
|
+
},
|
|
250
|
+
{ strapi }
|
|
251
|
+
);
|
|
252
|
+
releasesAffected.add(action.release.id);
|
|
253
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
254
|
+
where: {
|
|
255
|
+
id: action.id
|
|
256
|
+
},
|
|
257
|
+
data: {
|
|
258
|
+
isEntryValid: isEntryValid2
|
|
259
|
+
}
|
|
293
260
|
});
|
|
261
|
+
}
|
|
294
262
|
});
|
|
295
|
-
|
|
263
|
+
}
|
|
264
|
+
}).then(() => {
|
|
265
|
+
async.map(releasesAffected, async (releaseId) => {
|
|
266
|
+
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
296
270
|
}
|
|
297
|
-
async function disableContentTypeLocalized({ oldContentTypes, contentTypes }) {
|
|
298
|
-
|
|
299
|
-
|
|
271
|
+
async function disableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
272
|
+
if (!oldContentTypes) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
276
|
+
if (!i18nPlugin) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
for (const uid in contentTypes2) {
|
|
280
|
+
if (!oldContentTypes[uid]) {
|
|
281
|
+
continue;
|
|
300
282
|
}
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
const oldContentType = oldContentTypes[uid];
|
|
310
|
-
const contentType = contentTypes[uid];
|
|
311
|
-
const { isLocalizedContentType } = i18nPlugin.service('content-types');
|
|
312
|
-
// if i18N is disabled remove non default locales before sync
|
|
313
|
-
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
314
|
-
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
315
|
-
locale: null
|
|
316
|
-
}).where({
|
|
317
|
-
contentType: uid
|
|
318
|
-
}).execute();
|
|
319
|
-
}
|
|
283
|
+
const oldContentType = oldContentTypes[uid];
|
|
284
|
+
const contentType = contentTypes2[uid];
|
|
285
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
286
|
+
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
287
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
288
|
+
locale: null
|
|
289
|
+
}).where({ contentType: uid }).execute();
|
|
320
290
|
}
|
|
291
|
+
}
|
|
321
292
|
}
|
|
322
|
-
async function enableContentTypeLocalized({ oldContentTypes, contentTypes }) {
|
|
323
|
-
|
|
324
|
-
|
|
293
|
+
async function enableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
294
|
+
if (!oldContentTypes) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
298
|
+
if (!i18nPlugin) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
for (const uid in contentTypes2) {
|
|
302
|
+
if (!oldContentTypes[uid]) {
|
|
303
|
+
continue;
|
|
325
304
|
}
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const contentType = contentTypes[uid];
|
|
336
|
-
const { isLocalizedContentType } = i18nPlugin.service('content-types');
|
|
337
|
-
const { getDefaultLocale } = i18nPlugin.service('locales');
|
|
338
|
-
// if i18N is enabled remove non default locales before sync
|
|
339
|
-
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
340
|
-
const defaultLocale = await getDefaultLocale();
|
|
341
|
-
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
342
|
-
locale: defaultLocale
|
|
343
|
-
}).where({
|
|
344
|
-
contentType: uid
|
|
345
|
-
}).execute();
|
|
346
|
-
}
|
|
305
|
+
const oldContentType = oldContentTypes[uid];
|
|
306
|
+
const contentType = contentTypes2[uid];
|
|
307
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
308
|
+
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
309
|
+
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
310
|
+
const defaultLocale = await getDefaultLocale();
|
|
311
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
312
|
+
locale: defaultLocale
|
|
313
|
+
}).where({ contentType: uid }).execute();
|
|
347
314
|
}
|
|
315
|
+
}
|
|
348
316
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
}
|
|
374
|
-
const releaseActions = await trx.select('*').from('strapi_release_actions');
|
|
375
|
-
async.map(releaseActions, async (action)=>{
|
|
376
|
-
const { target_type, target_id } = action;
|
|
377
|
-
const entry = await db.query(target_type).findOne({
|
|
378
|
-
where: {
|
|
379
|
-
id: target_id
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
if (entry) {
|
|
383
|
-
await trx('strapi_release_actions').update({
|
|
384
|
-
entry_document_id: entry.documentId
|
|
385
|
-
}).where('id', action.id);
|
|
386
|
-
}
|
|
387
|
-
});
|
|
317
|
+
const addEntryDocumentToReleaseActions = {
|
|
318
|
+
name: "content-releases::5.0.0-add-entry-document-id-to-release-actions",
|
|
319
|
+
async up(trx, db) {
|
|
320
|
+
const hasTable = await trx.schema.hasTable("strapi_release_actions");
|
|
321
|
+
if (!hasTable) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
|
|
325
|
+
if (hasPolymorphicColumn) {
|
|
326
|
+
const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
|
|
327
|
+
"strapi_release_actions",
|
|
328
|
+
"entry_document_id"
|
|
329
|
+
);
|
|
330
|
+
if (!hasEntryDocumentIdColumn) {
|
|
331
|
+
await trx.schema.alterTable("strapi_release_actions", (table) => {
|
|
332
|
+
table.string("entry_document_id");
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const releaseActions = await trx.select("*").from("strapi_release_actions");
|
|
336
|
+
async.map(releaseActions, async (action) => {
|
|
337
|
+
const { target_type, target_id } = action;
|
|
338
|
+
const entry = await db.query(target_type).findOne({ where: { id: target_id } });
|
|
339
|
+
if (entry) {
|
|
340
|
+
await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
|
|
388
341
|
}
|
|
389
|
-
|
|
390
|
-
async down () {
|
|
391
|
-
throw new Error('not implemented');
|
|
342
|
+
});
|
|
392
343
|
}
|
|
344
|
+
},
|
|
345
|
+
async down() {
|
|
346
|
+
throw new Error("not implemented");
|
|
347
|
+
}
|
|
393
348
|
};
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
349
|
+
const register = async ({ strapi: strapi2 }) => {
|
|
350
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
351
|
+
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
352
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
353
|
+
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
354
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
355
|
+
}
|
|
356
|
+
if (strapi2.plugin("graphql")) {
|
|
357
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
358
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
359
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
363
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
364
|
+
where: {
|
|
365
|
+
releasedAt: null,
|
|
366
|
+
actions: {
|
|
367
|
+
contentType,
|
|
368
|
+
entryDocumentId: entry.documentId,
|
|
369
|
+
locale: entry.locale
|
|
370
|
+
}
|
|
401
371
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
372
|
+
});
|
|
373
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
374
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).updateMany({
|
|
375
|
+
where: {
|
|
376
|
+
contentType,
|
|
377
|
+
entryDocumentId: entry.documentId,
|
|
378
|
+
locale: entry.locale
|
|
379
|
+
},
|
|
380
|
+
data: {
|
|
381
|
+
isEntryValid: entryStatus
|
|
407
382
|
}
|
|
383
|
+
});
|
|
384
|
+
for (const release2 of releases) {
|
|
385
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
386
|
+
}
|
|
408
387
|
};
|
|
409
|
-
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
releasedAt: null,
|
|
414
|
-
actions: {
|
|
415
|
-
contentType,
|
|
416
|
-
entryDocumentId: entry.documentId,
|
|
417
|
-
locale: entry.locale
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
const entryStatus = await isEntryValid(contentType, entry, {
|
|
422
|
-
strapi
|
|
423
|
-
});
|
|
424
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).updateMany({
|
|
425
|
-
where: {
|
|
426
|
-
contentType,
|
|
427
|
-
entryDocumentId: entry.documentId,
|
|
428
|
-
locale: entry.locale
|
|
429
|
-
},
|
|
430
|
-
data: {
|
|
431
|
-
isEntryValid: entryStatus
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
for (const release of releases){
|
|
435
|
-
getService('release', {
|
|
436
|
-
strapi
|
|
437
|
-
}).updateReleaseStatus(release.id);
|
|
388
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
389
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
390
|
+
where: {
|
|
391
|
+
actions: params
|
|
438
392
|
}
|
|
393
|
+
});
|
|
394
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
395
|
+
where: params
|
|
396
|
+
});
|
|
397
|
+
for (const release2 of releases) {
|
|
398
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
399
|
+
}
|
|
439
400
|
};
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
401
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
402
|
+
if (ctx.action !== "delete") {
|
|
403
|
+
return next();
|
|
404
|
+
}
|
|
405
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
406
|
+
return next();
|
|
407
|
+
}
|
|
408
|
+
const contentType = ctx.contentType.uid;
|
|
409
|
+
const { documentId, locale } = ctx.params;
|
|
410
|
+
const result = await next();
|
|
411
|
+
if (!result) {
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
416
|
+
contentType,
|
|
417
|
+
entryDocumentId: documentId,
|
|
418
|
+
...locale !== "*" && { locale }
|
|
445
419
|
});
|
|
446
|
-
|
|
447
|
-
|
|
420
|
+
} catch (error) {
|
|
421
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
422
|
+
error
|
|
448
423
|
});
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
strapi
|
|
452
|
-
}).updateReleaseStatus(release.id);
|
|
453
|
-
}
|
|
424
|
+
}
|
|
425
|
+
return result;
|
|
454
426
|
};
|
|
455
|
-
const
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
if (!result) {
|
|
466
|
-
return result;
|
|
467
|
-
}
|
|
468
|
-
try {
|
|
469
|
-
deleteActionsAndUpdateReleaseStatus({
|
|
470
|
-
contentType,
|
|
471
|
-
entryDocumentId: documentId,
|
|
472
|
-
...locale !== '*' && {
|
|
473
|
-
locale
|
|
474
|
-
}
|
|
475
|
-
});
|
|
476
|
-
} catch (error) {
|
|
477
|
-
strapi.log.error('Error while deleting release actions after delete', {
|
|
478
|
-
error
|
|
479
|
-
});
|
|
480
|
-
}
|
|
427
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
428
|
+
if (ctx.action !== "update") {
|
|
429
|
+
return next();
|
|
430
|
+
}
|
|
431
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
432
|
+
return next();
|
|
433
|
+
}
|
|
434
|
+
const contentType = ctx.contentType.uid;
|
|
435
|
+
const result = await next();
|
|
436
|
+
if (!result) {
|
|
481
437
|
return result;
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
441
|
+
} catch (error) {
|
|
442
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
443
|
+
error
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return result;
|
|
482
447
|
};
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
488
|
-
return next();
|
|
489
|
-
}
|
|
490
|
-
const contentType = ctx.contentType.uid;
|
|
491
|
-
const result = await next();
|
|
492
|
-
if (!result) {
|
|
493
|
-
return result;
|
|
448
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
449
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
450
|
+
where: {
|
|
451
|
+
actions: params
|
|
494
452
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
return result;
|
|
453
|
+
});
|
|
454
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
455
|
+
where: params
|
|
456
|
+
});
|
|
457
|
+
for (const release2 of releases) {
|
|
458
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
459
|
+
}
|
|
503
460
|
};
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
461
|
+
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
462
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
463
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
464
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
465
|
+
);
|
|
466
|
+
strapi2.db.lifecycles.subscribe({
|
|
467
|
+
models: contentTypesWithDraftAndPublish,
|
|
468
|
+
/**
|
|
469
|
+
* deleteMany is still used outside documents service, for example when deleting a locale
|
|
470
|
+
*/
|
|
471
|
+
async afterDeleteMany(event) {
|
|
472
|
+
try {
|
|
473
|
+
const model = strapi2.getModel(event.model.uid);
|
|
474
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
475
|
+
const { where } = event.params;
|
|
476
|
+
deleteReleasesActionsAndUpdateReleaseStatus({
|
|
477
|
+
contentType: model.uid,
|
|
478
|
+
locale: where?.locale ?? null,
|
|
479
|
+
...where?.documentId && { entryDocumentId: where.documentId }
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
} catch (error) {
|
|
483
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
484
|
+
error
|
|
485
|
+
});
|
|
509
486
|
}
|
|
487
|
+
}
|
|
510
488
|
});
|
|
511
|
-
|
|
512
|
-
|
|
489
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
490
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
491
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
492
|
+
strapi2.log.error(
|
|
493
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
494
|
+
);
|
|
495
|
+
throw err;
|
|
513
496
|
});
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}).updateReleaseStatus(release.id);
|
|
519
|
-
}
|
|
497
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
498
|
+
strapi2.get("webhookStore").addAllowedEvent(key, value);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
520
501
|
};
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
*/ async afterDeleteMany (event) {
|
|
529
|
-
try {
|
|
530
|
-
const model = strapi1.getModel(event.model.uid);
|
|
531
|
-
// @ts-expect-error TODO: lifecycles types looks like are not 100% finished
|
|
532
|
-
if (model.kind === 'collectionType' && model.options?.draftAndPublish) {
|
|
533
|
-
const { where } = event.params;
|
|
534
|
-
deleteReleasesActionsAndUpdateReleaseStatus({
|
|
535
|
-
contentType: model.uid,
|
|
536
|
-
locale: where?.locale ?? null,
|
|
537
|
-
...where?.documentId && {
|
|
538
|
-
entryDocumentId: where.documentId
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
}
|
|
542
|
-
} catch (error) {
|
|
543
|
-
// If an error happens we don't want to block the delete entry flow, but we log the error
|
|
544
|
-
strapi1.log.error('Error while deleting release actions after entry deleteMany', {
|
|
545
|
-
error
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
// We register middleware to handle ReleaseActions when changes on documents are made
|
|
551
|
-
strapi1.documents.use(deleteActionsOnDelete);
|
|
552
|
-
strapi1.documents.use(updateActionsOnUpdate);
|
|
553
|
-
getService('scheduling', {
|
|
554
|
-
strapi: strapi1
|
|
555
|
-
}).syncFromDatabase().catch((err)=>{
|
|
556
|
-
strapi1.log.error('Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling.');
|
|
557
|
-
throw err;
|
|
558
|
-
});
|
|
559
|
-
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value])=>{
|
|
560
|
-
strapi1.get('webhookStore').addAllowedEvent(key, value);
|
|
561
|
-
});
|
|
562
|
-
}
|
|
502
|
+
const destroy = async ({ strapi: strapi2 }) => {
|
|
503
|
+
const scheduledJobs = getService("scheduling", {
|
|
504
|
+
strapi: strapi2
|
|
505
|
+
}).getAll();
|
|
506
|
+
for (const [, job] of scheduledJobs) {
|
|
507
|
+
job.cancel();
|
|
508
|
+
}
|
|
563
509
|
};
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
510
|
+
const schema$1 = {
|
|
511
|
+
collectionName: "strapi_releases",
|
|
512
|
+
info: {
|
|
513
|
+
singularName: "release",
|
|
514
|
+
pluralName: "releases",
|
|
515
|
+
displayName: "Release"
|
|
516
|
+
},
|
|
517
|
+
options: {
|
|
518
|
+
draftAndPublish: false
|
|
519
|
+
},
|
|
520
|
+
pluginOptions: {
|
|
521
|
+
"content-manager": {
|
|
522
|
+
visible: false
|
|
523
|
+
},
|
|
524
|
+
"content-type-builder": {
|
|
525
|
+
visible: false
|
|
571
526
|
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
singularName: 'release',
|
|
578
|
-
pluralName: 'releases',
|
|
579
|
-
displayName: 'Release'
|
|
527
|
+
},
|
|
528
|
+
attributes: {
|
|
529
|
+
name: {
|
|
530
|
+
type: "string",
|
|
531
|
+
required: true
|
|
580
532
|
},
|
|
581
|
-
|
|
582
|
-
|
|
533
|
+
releasedAt: {
|
|
534
|
+
type: "datetime"
|
|
583
535
|
},
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
visible: false
|
|
587
|
-
},
|
|
588
|
-
'content-type-builder': {
|
|
589
|
-
visible: false
|
|
590
|
-
}
|
|
536
|
+
scheduledAt: {
|
|
537
|
+
type: "datetime"
|
|
591
538
|
},
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
},
|
|
606
|
-
status: {
|
|
607
|
-
type: 'enumeration',
|
|
608
|
-
enum: [
|
|
609
|
-
'ready',
|
|
610
|
-
'blocked',
|
|
611
|
-
'failed',
|
|
612
|
-
'done',
|
|
613
|
-
'empty'
|
|
614
|
-
],
|
|
615
|
-
required: true
|
|
616
|
-
},
|
|
617
|
-
actions: {
|
|
618
|
-
type: 'relation',
|
|
619
|
-
relation: 'oneToMany',
|
|
620
|
-
target: RELEASE_ACTION_MODEL_UID,
|
|
621
|
-
mappedBy: 'release'
|
|
622
|
-
}
|
|
539
|
+
timezone: {
|
|
540
|
+
type: "string"
|
|
541
|
+
},
|
|
542
|
+
status: {
|
|
543
|
+
type: "enumeration",
|
|
544
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
545
|
+
required: true
|
|
546
|
+
},
|
|
547
|
+
actions: {
|
|
548
|
+
type: "relation",
|
|
549
|
+
relation: "oneToMany",
|
|
550
|
+
target: RELEASE_ACTION_MODEL_UID,
|
|
551
|
+
mappedBy: "release"
|
|
623
552
|
}
|
|
553
|
+
}
|
|
624
554
|
};
|
|
625
|
-
|
|
626
555
|
const release$1 = {
|
|
627
|
-
|
|
556
|
+
schema: schema$1
|
|
628
557
|
};
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
558
|
+
const schema = {
|
|
559
|
+
collectionName: "strapi_release_actions",
|
|
560
|
+
info: {
|
|
561
|
+
singularName: "release-action",
|
|
562
|
+
pluralName: "release-actions",
|
|
563
|
+
displayName: "Release Action"
|
|
564
|
+
},
|
|
565
|
+
options: {
|
|
566
|
+
draftAndPublish: false
|
|
567
|
+
},
|
|
568
|
+
pluginOptions: {
|
|
569
|
+
"content-manager": {
|
|
570
|
+
visible: false
|
|
636
571
|
},
|
|
637
|
-
|
|
638
|
-
|
|
572
|
+
"content-type-builder": {
|
|
573
|
+
visible: false
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
attributes: {
|
|
577
|
+
type: {
|
|
578
|
+
type: "enumeration",
|
|
579
|
+
enum: ["publish", "unpublish"],
|
|
580
|
+
required: true
|
|
639
581
|
},
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
},
|
|
644
|
-
'content-type-builder': {
|
|
645
|
-
visible: false
|
|
646
|
-
}
|
|
582
|
+
contentType: {
|
|
583
|
+
type: "string",
|
|
584
|
+
required: true
|
|
647
585
|
},
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
type: 'string'
|
|
663
|
-
},
|
|
664
|
-
locale: {
|
|
665
|
-
type: 'string'
|
|
666
|
-
},
|
|
667
|
-
release: {
|
|
668
|
-
type: 'relation',
|
|
669
|
-
relation: 'manyToOne',
|
|
670
|
-
target: RELEASE_MODEL_UID,
|
|
671
|
-
inversedBy: 'actions'
|
|
672
|
-
},
|
|
673
|
-
isEntryValid: {
|
|
674
|
-
type: 'boolean'
|
|
675
|
-
}
|
|
586
|
+
entryDocumentId: {
|
|
587
|
+
type: "string"
|
|
588
|
+
},
|
|
589
|
+
locale: {
|
|
590
|
+
type: "string"
|
|
591
|
+
},
|
|
592
|
+
release: {
|
|
593
|
+
type: "relation",
|
|
594
|
+
relation: "manyToOne",
|
|
595
|
+
target: RELEASE_MODEL_UID,
|
|
596
|
+
inversedBy: "actions"
|
|
597
|
+
},
|
|
598
|
+
isEntryValid: {
|
|
599
|
+
type: "boolean"
|
|
676
600
|
}
|
|
601
|
+
}
|
|
677
602
|
};
|
|
678
|
-
|
|
679
603
|
const releaseAction$1 = {
|
|
680
|
-
|
|
604
|
+
schema
|
|
681
605
|
};
|
|
682
|
-
|
|
683
606
|
const contentTypes = {
|
|
684
|
-
|
|
685
|
-
|
|
607
|
+
release: release$1,
|
|
608
|
+
"release-action": releaseAction$1
|
|
686
609
|
};
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
610
|
+
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
611
|
+
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
612
|
+
strapi2.eventHub.emit(event, {
|
|
613
|
+
isPublished,
|
|
614
|
+
error,
|
|
615
|
+
release: release2
|
|
616
|
+
});
|
|
617
|
+
};
|
|
618
|
+
const getFormattedActions = async (releaseId) => {
|
|
619
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
620
|
+
where: {
|
|
621
|
+
release: {
|
|
622
|
+
id: releaseId
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
if (actions.length === 0) {
|
|
627
|
+
throw new errors.ValidationError("No entries to publish");
|
|
628
|
+
}
|
|
629
|
+
const formattedActions = {};
|
|
630
|
+
for (const action of actions) {
|
|
631
|
+
const contentTypeUid = action.contentType;
|
|
632
|
+
if (!formattedActions[contentTypeUid]) {
|
|
633
|
+
formattedActions[contentTypeUid] = {
|
|
634
|
+
publish: [],
|
|
635
|
+
unpublish: []
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
639
|
+
documentId: action.entryDocumentId,
|
|
640
|
+
locale: action.locale
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
return formattedActions;
|
|
644
|
+
};
|
|
645
|
+
return {
|
|
646
|
+
async create(releaseData, { user }) {
|
|
647
|
+
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
648
|
+
const {
|
|
649
|
+
validatePendingReleasesLimit,
|
|
650
|
+
validateUniqueNameForPendingRelease,
|
|
651
|
+
validateScheduledAtIsLaterThanNow
|
|
652
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
653
|
+
await Promise.all([
|
|
654
|
+
validatePendingReleasesLimit(),
|
|
655
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
656
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
657
|
+
]);
|
|
658
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
659
|
+
data: {
|
|
660
|
+
...releaseWithCreatorFields,
|
|
661
|
+
status: "empty"
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
665
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
666
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
667
|
+
}
|
|
668
|
+
strapi2.telemetry.send("didCreateContentRelease");
|
|
669
|
+
return release2;
|
|
670
|
+
},
|
|
671
|
+
async findOne(id, query = {}) {
|
|
672
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
|
|
673
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
674
|
+
...dbQuery,
|
|
675
|
+
where: { id }
|
|
676
|
+
});
|
|
677
|
+
return release2;
|
|
678
|
+
},
|
|
679
|
+
findPage(query) {
|
|
680
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
681
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
682
|
+
...dbQuery,
|
|
683
|
+
populate: {
|
|
684
|
+
actions: {
|
|
685
|
+
count: true
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
},
|
|
690
|
+
findMany(query) {
|
|
691
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
692
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
693
|
+
...dbQuery
|
|
694
|
+
});
|
|
695
|
+
},
|
|
696
|
+
async update(id, releaseData, { user }) {
|
|
697
|
+
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(
|
|
698
|
+
releaseData
|
|
699
|
+
);
|
|
700
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
701
|
+
"release-validation",
|
|
702
|
+
{ strapi: strapi2 }
|
|
703
|
+
);
|
|
704
|
+
await Promise.all([
|
|
705
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
706
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
707
|
+
]);
|
|
708
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
709
|
+
if (!release2) {
|
|
710
|
+
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
711
|
+
}
|
|
712
|
+
if (release2.releasedAt) {
|
|
713
|
+
throw new errors.ValidationError("Release already published");
|
|
714
|
+
}
|
|
715
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
716
|
+
where: { id },
|
|
717
|
+
data: releaseWithCreatorFields
|
|
718
|
+
});
|
|
719
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
720
|
+
if (releaseData.scheduledAt) {
|
|
721
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
722
|
+
} else if (release2.scheduledAt) {
|
|
723
|
+
schedulingService.cancel(id);
|
|
724
|
+
}
|
|
725
|
+
this.updateReleaseStatus(id);
|
|
726
|
+
strapi2.telemetry.send("didUpdateContentRelease");
|
|
727
|
+
return updatedRelease;
|
|
728
|
+
},
|
|
729
|
+
async getAllComponents() {
|
|
730
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
731
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
732
|
+
const componentsMap = components.reduce(
|
|
733
|
+
(acc, component) => {
|
|
734
|
+
acc[component.uid] = component;
|
|
735
|
+
return acc;
|
|
736
|
+
},
|
|
737
|
+
{}
|
|
738
|
+
);
|
|
739
|
+
return componentsMap;
|
|
740
|
+
},
|
|
741
|
+
async delete(releaseId) {
|
|
742
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
743
|
+
where: { id: releaseId },
|
|
744
|
+
populate: {
|
|
745
|
+
actions: {
|
|
746
|
+
select: ["id"]
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
if (!release2) {
|
|
751
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
752
|
+
}
|
|
753
|
+
if (release2.releasedAt) {
|
|
754
|
+
throw new errors.ValidationError("Release already published");
|
|
755
|
+
}
|
|
756
|
+
await strapi2.db.transaction(async () => {
|
|
757
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
758
|
+
where: {
|
|
759
|
+
id: {
|
|
760
|
+
$in: release2.actions.map((action) => action.id)
|
|
761
|
+
}
|
|
762
|
+
}
|
|
694
763
|
});
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
*/ const getFormattedActions = async (releaseId)=>{
|
|
700
|
-
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
701
|
-
where: {
|
|
702
|
-
release: {
|
|
703
|
-
id: releaseId
|
|
704
|
-
}
|
|
705
|
-
}
|
|
764
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
765
|
+
where: {
|
|
766
|
+
id: releaseId
|
|
767
|
+
}
|
|
706
768
|
});
|
|
707
|
-
|
|
708
|
-
|
|
769
|
+
});
|
|
770
|
+
if (release2.scheduledAt) {
|
|
771
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
772
|
+
await schedulingService.cancel(release2.id);
|
|
773
|
+
}
|
|
774
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
775
|
+
return release2;
|
|
776
|
+
},
|
|
777
|
+
async publish(releaseId) {
|
|
778
|
+
const {
|
|
779
|
+
release: release2,
|
|
780
|
+
error
|
|
781
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
782
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
783
|
+
if (!lockedRelease) {
|
|
784
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
709
785
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
*/ const formattedActions = {};
|
|
713
|
-
for (const action of actions){
|
|
714
|
-
const contentTypeUid = action.contentType;
|
|
715
|
-
if (!formattedActions[contentTypeUid]) {
|
|
716
|
-
formattedActions[contentTypeUid] = {
|
|
717
|
-
publish: [],
|
|
718
|
-
unpublish: []
|
|
719
|
-
};
|
|
720
|
-
}
|
|
721
|
-
formattedActions[contentTypeUid][action.type].push({
|
|
722
|
-
documentId: action.entryDocumentId,
|
|
723
|
-
locale: action.locale
|
|
724
|
-
});
|
|
786
|
+
if (lockedRelease.releasedAt) {
|
|
787
|
+
throw new errors.ValidationError("Release already published");
|
|
725
788
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
802
|
-
}
|
|
803
|
-
if (release.releasedAt) {
|
|
804
|
-
throw new errors.ValidationError('Release already published');
|
|
805
|
-
}
|
|
806
|
-
const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({
|
|
807
|
-
where: {
|
|
808
|
-
id
|
|
809
|
-
},
|
|
810
|
-
data: releaseWithCreatorFields
|
|
811
|
-
});
|
|
812
|
-
const schedulingService = getService('scheduling', {
|
|
813
|
-
strapi
|
|
814
|
-
});
|
|
815
|
-
if (releaseData.scheduledAt) {
|
|
816
|
-
// set function always cancel the previous job if it exists, so we can call it directly
|
|
817
|
-
await schedulingService.set(id, releaseData.scheduledAt);
|
|
818
|
-
} else if (release.scheduledAt) {
|
|
819
|
-
// When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it
|
|
820
|
-
schedulingService.cancel(id);
|
|
821
|
-
}
|
|
822
|
-
this.updateReleaseStatus(id);
|
|
823
|
-
strapi.telemetry.send('didUpdateContentRelease');
|
|
824
|
-
return updatedRelease;
|
|
825
|
-
},
|
|
826
|
-
async getAllComponents () {
|
|
827
|
-
const contentManagerComponentsService = strapi.plugin('content-manager').service('components');
|
|
828
|
-
const components = await contentManagerComponentsService.findAllComponents();
|
|
829
|
-
const componentsMap = components.reduce((acc, component)=>{
|
|
830
|
-
acc[component.uid] = component;
|
|
831
|
-
return acc;
|
|
832
|
-
}, {});
|
|
833
|
-
return componentsMap;
|
|
834
|
-
},
|
|
835
|
-
async delete (releaseId) {
|
|
836
|
-
const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
|
|
837
|
-
where: {
|
|
838
|
-
id: releaseId
|
|
839
|
-
},
|
|
840
|
-
populate: {
|
|
841
|
-
actions: {
|
|
842
|
-
select: [
|
|
843
|
-
'id'
|
|
844
|
-
]
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
});
|
|
848
|
-
if (!release) {
|
|
849
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
850
|
-
}
|
|
851
|
-
if (release.releasedAt) {
|
|
852
|
-
throw new errors.ValidationError('Release already published');
|
|
853
|
-
}
|
|
854
|
-
// Only delete the release and its actions is you in fact can delete all the actions and the release
|
|
855
|
-
// Otherwise, if the transaction fails it throws an error
|
|
856
|
-
await strapi.db.transaction(async ()=>{
|
|
857
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
858
|
-
where: {
|
|
859
|
-
id: {
|
|
860
|
-
$in: release.actions.map((action)=>action.id)
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
});
|
|
864
|
-
await strapi.db.query(RELEASE_MODEL_UID).delete({
|
|
865
|
-
where: {
|
|
866
|
-
id: releaseId
|
|
867
|
-
}
|
|
868
|
-
});
|
|
869
|
-
});
|
|
870
|
-
if (release.scheduledAt) {
|
|
871
|
-
const schedulingService = getService('scheduling', {
|
|
872
|
-
strapi
|
|
873
|
-
});
|
|
874
|
-
await schedulingService.cancel(release.id);
|
|
875
|
-
}
|
|
876
|
-
strapi.telemetry.send('didDeleteContentRelease');
|
|
877
|
-
return release;
|
|
878
|
-
},
|
|
879
|
-
async publish (releaseId) {
|
|
880
|
-
const { release, error } = await strapi.db.transaction(async ({ trx })=>{
|
|
881
|
-
/**
|
|
882
|
-
* We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished
|
|
883
|
-
* In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed
|
|
884
|
-
*/ const lockedRelease = await strapi.db?.queryBuilder(RELEASE_MODEL_UID).where({
|
|
885
|
-
id: releaseId
|
|
886
|
-
}).select([
|
|
887
|
-
'id',
|
|
888
|
-
'name',
|
|
889
|
-
'releasedAt',
|
|
890
|
-
'status'
|
|
891
|
-
]).first().transacting(trx).forUpdate().execute();
|
|
892
|
-
if (!lockedRelease) {
|
|
893
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
894
|
-
}
|
|
895
|
-
if (lockedRelease.releasedAt) {
|
|
896
|
-
throw new errors.ValidationError('Release already published');
|
|
897
|
-
}
|
|
898
|
-
if (lockedRelease.status === 'failed') {
|
|
899
|
-
throw new errors.ValidationError('Release failed to publish');
|
|
900
|
-
}
|
|
901
|
-
try {
|
|
902
|
-
strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
903
|
-
const formattedActions = await getFormattedActions(releaseId);
|
|
904
|
-
await strapi.db.transaction(async ()=>Promise.all(Object.keys(formattedActions).map(async (contentTypeUid)=>{
|
|
905
|
-
const contentType = contentTypeUid;
|
|
906
|
-
const { publish, unpublish } = formattedActions[contentType];
|
|
907
|
-
return Promise.all([
|
|
908
|
-
...publish.map((params)=>strapi.documents(contentType).publish(params)),
|
|
909
|
-
...unpublish.map((params)=>strapi.documents(contentType).unpublish(params))
|
|
910
|
-
]);
|
|
911
|
-
})));
|
|
912
|
-
const release = await strapi.db.query(RELEASE_MODEL_UID).update({
|
|
913
|
-
where: {
|
|
914
|
-
id: releaseId
|
|
915
|
-
},
|
|
916
|
-
data: {
|
|
917
|
-
status: 'done',
|
|
918
|
-
releasedAt: new Date()
|
|
919
|
-
}
|
|
920
|
-
});
|
|
921
|
-
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
922
|
-
isPublished: true,
|
|
923
|
-
release
|
|
924
|
-
});
|
|
925
|
-
strapi.telemetry.send('didPublishContentRelease');
|
|
926
|
-
return {
|
|
927
|
-
release,
|
|
928
|
-
error: null
|
|
929
|
-
};
|
|
930
|
-
} catch (error) {
|
|
931
|
-
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
932
|
-
isPublished: false,
|
|
933
|
-
error
|
|
934
|
-
});
|
|
935
|
-
// We need to run the update in the same transaction because the release is locked
|
|
936
|
-
await strapi.db?.queryBuilder(RELEASE_MODEL_UID).where({
|
|
937
|
-
id: releaseId
|
|
938
|
-
}).update({
|
|
939
|
-
status: 'failed'
|
|
940
|
-
}).transacting(trx).execute();
|
|
941
|
-
// At this point, we don't want to throw the error because if that happen we rollback the change in the release status
|
|
942
|
-
// We want to throw the error after the transaction is finished, so we return the error
|
|
943
|
-
return {
|
|
944
|
-
release: null,
|
|
945
|
-
error
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
});
|
|
949
|
-
// Now the first transaction is commited, we can safely throw the error if it exists
|
|
950
|
-
if (error instanceof Error) {
|
|
951
|
-
throw error;
|
|
789
|
+
if (lockedRelease.status === "failed") {
|
|
790
|
+
throw new errors.ValidationError("Release failed to publish");
|
|
791
|
+
}
|
|
792
|
+
try {
|
|
793
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
794
|
+
const formattedActions = await getFormattedActions(releaseId);
|
|
795
|
+
await strapi2.db.transaction(
|
|
796
|
+
async () => Promise.all(
|
|
797
|
+
Object.keys(formattedActions).map(async (contentTypeUid) => {
|
|
798
|
+
const contentType = contentTypeUid;
|
|
799
|
+
const { publish, unpublish } = formattedActions[contentType];
|
|
800
|
+
return Promise.all([
|
|
801
|
+
...publish.map((params) => strapi2.documents(contentType).publish(params)),
|
|
802
|
+
...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
|
|
803
|
+
]);
|
|
804
|
+
})
|
|
805
|
+
)
|
|
806
|
+
);
|
|
807
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
808
|
+
where: {
|
|
809
|
+
id: releaseId
|
|
810
|
+
},
|
|
811
|
+
data: {
|
|
812
|
+
status: "done",
|
|
813
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
817
|
+
isPublished: true,
|
|
818
|
+
release: release22
|
|
819
|
+
});
|
|
820
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
821
|
+
return { release: release22, error: null };
|
|
822
|
+
} catch (error2) {
|
|
823
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
824
|
+
isPublished: false,
|
|
825
|
+
error: error2
|
|
826
|
+
});
|
|
827
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
828
|
+
status: "failed"
|
|
829
|
+
}).transacting(trx).execute();
|
|
830
|
+
return {
|
|
831
|
+
release: null,
|
|
832
|
+
error: error2
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
});
|
|
836
|
+
if (error instanceof Error) {
|
|
837
|
+
throw error;
|
|
838
|
+
}
|
|
839
|
+
return release2;
|
|
840
|
+
},
|
|
841
|
+
async updateReleaseStatus(releaseId) {
|
|
842
|
+
const releaseActionService = getService("release-action", { strapi: strapi2 });
|
|
843
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
844
|
+
releaseActionService.countActions({
|
|
845
|
+
filters: {
|
|
846
|
+
release: releaseId
|
|
847
|
+
}
|
|
848
|
+
}),
|
|
849
|
+
releaseActionService.countActions({
|
|
850
|
+
filters: {
|
|
851
|
+
release: releaseId,
|
|
852
|
+
isEntryValid: false
|
|
853
|
+
}
|
|
854
|
+
})
|
|
855
|
+
]);
|
|
856
|
+
if (totalActions > 0) {
|
|
857
|
+
if (invalidActions > 0) {
|
|
858
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
859
|
+
where: {
|
|
860
|
+
id: releaseId
|
|
861
|
+
},
|
|
862
|
+
data: {
|
|
863
|
+
status: "blocked"
|
|
952
864
|
}
|
|
953
|
-
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
868
|
+
where: {
|
|
869
|
+
id: releaseId
|
|
870
|
+
},
|
|
871
|
+
data: {
|
|
872
|
+
status: "ready"
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
877
|
+
where: {
|
|
878
|
+
id: releaseId
|
|
954
879
|
},
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
strapi
|
|
958
|
-
});
|
|
959
|
-
const [totalActions, invalidActions] = await Promise.all([
|
|
960
|
-
releaseActionService.countActions({
|
|
961
|
-
filters: {
|
|
962
|
-
release: releaseId
|
|
963
|
-
}
|
|
964
|
-
}),
|
|
965
|
-
releaseActionService.countActions({
|
|
966
|
-
filters: {
|
|
967
|
-
release: releaseId,
|
|
968
|
-
isEntryValid: false
|
|
969
|
-
}
|
|
970
|
-
})
|
|
971
|
-
]);
|
|
972
|
-
if (totalActions > 0) {
|
|
973
|
-
if (invalidActions > 0) {
|
|
974
|
-
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
975
|
-
where: {
|
|
976
|
-
id: releaseId
|
|
977
|
-
},
|
|
978
|
-
data: {
|
|
979
|
-
status: 'blocked'
|
|
980
|
-
}
|
|
981
|
-
});
|
|
982
|
-
}
|
|
983
|
-
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
984
|
-
where: {
|
|
985
|
-
id: releaseId
|
|
986
|
-
},
|
|
987
|
-
data: {
|
|
988
|
-
status: 'ready'
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
993
|
-
where: {
|
|
994
|
-
id: releaseId
|
|
995
|
-
},
|
|
996
|
-
data: {
|
|
997
|
-
status: 'empty'
|
|
998
|
-
}
|
|
999
|
-
});
|
|
880
|
+
data: {
|
|
881
|
+
status: "empty"
|
|
1000
882
|
}
|
|
1001
|
-
|
|
1002
|
-
};
|
|
1003
|
-
|
|
1004
|
-
const getGroupName = (queryValue)=>{
|
|
1005
|
-
switch(queryValue){
|
|
1006
|
-
case 'contentType':
|
|
1007
|
-
return 'contentType.displayName';
|
|
1008
|
-
case 'type':
|
|
1009
|
-
return 'type';
|
|
1010
|
-
case 'locale':
|
|
1011
|
-
return _.getOr('No locale', 'locale.name');
|
|
1012
|
-
default:
|
|
1013
|
-
return 'contentType.displayName';
|
|
883
|
+
});
|
|
1014
884
|
}
|
|
885
|
+
};
|
|
1015
886
|
};
|
|
1016
|
-
const
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
887
|
+
const getGroupName = (queryValue) => {
|
|
888
|
+
switch (queryValue) {
|
|
889
|
+
case "contentType":
|
|
890
|
+
return "contentType.displayName";
|
|
891
|
+
case "type":
|
|
892
|
+
return "type";
|
|
893
|
+
case "locale":
|
|
894
|
+
return _.getOr("No locale", "locale.name");
|
|
895
|
+
default:
|
|
896
|
+
return "contentType.displayName";
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
const createReleaseActionService = ({ strapi: strapi2 }) => {
|
|
900
|
+
const getLocalesDataForActions = async () => {
|
|
901
|
+
if (!strapi2.plugin("i18n")) {
|
|
902
|
+
return {};
|
|
903
|
+
}
|
|
904
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
905
|
+
return allLocales.reduce((acc, locale) => {
|
|
906
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
907
|
+
return acc;
|
|
908
|
+
}, {});
|
|
909
|
+
};
|
|
910
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
911
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
912
|
+
const contentTypesData = {};
|
|
913
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
914
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
915
|
+
uid: contentTypeUid
|
|
916
|
+
});
|
|
917
|
+
contentTypesData[contentTypeUid] = {
|
|
918
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
919
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
return contentTypesData;
|
|
923
|
+
};
|
|
924
|
+
return {
|
|
925
|
+
async create(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
|
|
926
|
+
const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
|
|
927
|
+
strapi: strapi2
|
|
928
|
+
});
|
|
929
|
+
await Promise.all([
|
|
930
|
+
validateEntryData(action.contentType, action.entryDocumentId),
|
|
931
|
+
validateUniqueEntry(releaseId, action)
|
|
932
|
+
]);
|
|
933
|
+
const model = strapi2.contentType(action.contentType);
|
|
934
|
+
if (model.kind === "singleType") {
|
|
935
|
+
const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
936
|
+
if (!document) {
|
|
937
|
+
throw new errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
1041
938
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
const document = await strapi.db.query(model.uid).findOne({
|
|
1057
|
-
select: [
|
|
1058
|
-
'documentId'
|
|
1059
|
-
]
|
|
1060
|
-
});
|
|
1061
|
-
if (!document) {
|
|
1062
|
-
throw new errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
1063
|
-
}
|
|
1064
|
-
action.entryDocumentId = document.documentId;
|
|
1065
|
-
}
|
|
1066
|
-
const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
|
|
1067
|
-
where: {
|
|
1068
|
-
id: releaseId
|
|
1069
|
-
}
|
|
1070
|
-
});
|
|
1071
|
-
if (!release) {
|
|
1072
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1073
|
-
}
|
|
1074
|
-
if (release.releasedAt) {
|
|
1075
|
-
throw new errors.ValidationError('Release already published');
|
|
1076
|
-
}
|
|
1077
|
-
// If the action is a publish, check if the entry is valid
|
|
1078
|
-
// If the action is an unpublish, skip the validation
|
|
1079
|
-
const actionStatus = action.type === 'publish' ? await getDraftEntryValidStatus({
|
|
1080
|
-
contentType: action.contentType,
|
|
1081
|
-
documentId: action.entryDocumentId,
|
|
1082
|
-
locale: action.locale
|
|
1083
|
-
}, {
|
|
1084
|
-
strapi
|
|
1085
|
-
}) : true;
|
|
1086
|
-
const releaseAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
1087
|
-
data: {
|
|
1088
|
-
...action,
|
|
1089
|
-
release: release.id,
|
|
1090
|
-
isEntryValid: actionStatus
|
|
1091
|
-
},
|
|
1092
|
-
populate: {
|
|
1093
|
-
release: {
|
|
1094
|
-
select: [
|
|
1095
|
-
'id'
|
|
1096
|
-
]
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
});
|
|
1100
|
-
if (!disableUpdateReleaseStatus) {
|
|
1101
|
-
getService('release', {
|
|
1102
|
-
strapi
|
|
1103
|
-
}).updateReleaseStatus(release.id);
|
|
1104
|
-
}
|
|
1105
|
-
return releaseAction;
|
|
1106
|
-
},
|
|
1107
|
-
async findPage (releaseId, query) {
|
|
1108
|
-
const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
|
|
1109
|
-
where: {
|
|
1110
|
-
id: releaseId
|
|
1111
|
-
},
|
|
1112
|
-
select: [
|
|
1113
|
-
'id'
|
|
1114
|
-
]
|
|
1115
|
-
});
|
|
1116
|
-
if (!release) {
|
|
1117
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1118
|
-
}
|
|
1119
|
-
const dbQuery = strapi.get('query-params').transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1120
|
-
const { results: actions, pagination } = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
1121
|
-
...dbQuery,
|
|
1122
|
-
where: {
|
|
1123
|
-
release: releaseId
|
|
1124
|
-
}
|
|
1125
|
-
});
|
|
1126
|
-
// For each contentType on the release, we create a custom populate object for nested relations
|
|
1127
|
-
const populateBuilderService = strapi.plugin('content-manager').service('populate-builder');
|
|
1128
|
-
const actionsWithEntry = await async.map(actions, async (action)=>{
|
|
1129
|
-
// @ts-expect-error - Core.Service type is not a function
|
|
1130
|
-
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
1131
|
-
const entry = await getEntry({
|
|
1132
|
-
contentType: action.contentType,
|
|
1133
|
-
documentId: action.entryDocumentId,
|
|
1134
|
-
locale: action.locale,
|
|
1135
|
-
populate,
|
|
1136
|
-
status: action.type === 'publish' ? 'draft' : 'published'
|
|
1137
|
-
}, {
|
|
1138
|
-
strapi
|
|
1139
|
-
});
|
|
1140
|
-
return {
|
|
1141
|
-
...action,
|
|
1142
|
-
entry,
|
|
1143
|
-
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
1144
|
-
};
|
|
1145
|
-
});
|
|
1146
|
-
return {
|
|
1147
|
-
results: actionsWithEntry,
|
|
1148
|
-
pagination
|
|
1149
|
-
};
|
|
1150
|
-
},
|
|
1151
|
-
async groupActions (actions, groupBy) {
|
|
1152
|
-
const contentTypeUids = actions.reduce((acc, action)=>{
|
|
1153
|
-
if (!acc.includes(action.contentType)) {
|
|
1154
|
-
acc.push(action.contentType);
|
|
1155
|
-
}
|
|
1156
|
-
return acc;
|
|
1157
|
-
}, []);
|
|
1158
|
-
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1159
|
-
const allLocalesDictionary = await getLocalesDataForActions();
|
|
1160
|
-
const formattedData = actions.map((action)=>{
|
|
1161
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
1162
|
-
return {
|
|
1163
|
-
...action,
|
|
1164
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
1165
|
-
contentType: {
|
|
1166
|
-
displayName,
|
|
1167
|
-
mainFieldValue: action.entry[mainField],
|
|
1168
|
-
uid: action.contentType
|
|
1169
|
-
}
|
|
1170
|
-
};
|
|
1171
|
-
});
|
|
1172
|
-
const groupName = getGroupName(groupBy);
|
|
1173
|
-
return _.groupBy(groupName)(formattedData);
|
|
1174
|
-
},
|
|
1175
|
-
async getContentTypeModelsFromActions (actions) {
|
|
1176
|
-
const contentTypeUids = actions.reduce((acc, action)=>{
|
|
1177
|
-
if (!acc.includes(action.contentType)) {
|
|
1178
|
-
acc.push(action.contentType);
|
|
1179
|
-
}
|
|
1180
|
-
return acc;
|
|
1181
|
-
}, []);
|
|
1182
|
-
const workflowsService = strapi.plugin('review-workflows').service('workflows');
|
|
1183
|
-
const contentTypeModelsMap = await async.reduce(contentTypeUids)(async (accPromise, contentTypeUid)=>{
|
|
1184
|
-
const acc = await accPromise;
|
|
1185
|
-
const contentTypeModel = strapi.getModel(contentTypeUid);
|
|
1186
|
-
const workflow = await workflowsService.getAssignedWorkflow(contentTypeUid, {
|
|
1187
|
-
populate: 'stageRequiredToPublish'
|
|
1188
|
-
});
|
|
1189
|
-
acc[contentTypeUid] = {
|
|
1190
|
-
...contentTypeModel,
|
|
1191
|
-
hasReviewWorkflow: !!workflow,
|
|
1192
|
-
stageRequiredToPublish: workflow?.stageRequiredToPublish
|
|
1193
|
-
};
|
|
1194
|
-
return acc;
|
|
1195
|
-
}, {});
|
|
1196
|
-
return contentTypeModelsMap;
|
|
1197
|
-
},
|
|
1198
|
-
async countActions (query) {
|
|
1199
|
-
const dbQuery = strapi.get('query-params').transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1200
|
-
return strapi.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
1201
|
-
},
|
|
1202
|
-
async update (actionId, releaseId, update) {
|
|
1203
|
-
const action = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1204
|
-
where: {
|
|
1205
|
-
id: actionId,
|
|
1206
|
-
release: {
|
|
1207
|
-
id: releaseId,
|
|
1208
|
-
releasedAt: {
|
|
1209
|
-
$null: true
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
}
|
|
1213
|
-
});
|
|
1214
|
-
if (!action) {
|
|
1215
|
-
throw new errors.NotFoundError(`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`);
|
|
1216
|
-
}
|
|
1217
|
-
const actionStatus = update.type === 'publish' ? await getDraftEntryValidStatus({
|
|
1218
|
-
contentType: action.contentType,
|
|
1219
|
-
documentId: action.entryDocumentId,
|
|
1220
|
-
locale: action.locale
|
|
1221
|
-
}, {
|
|
1222
|
-
strapi
|
|
1223
|
-
}) : true;
|
|
1224
|
-
const updatedAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1225
|
-
where: {
|
|
1226
|
-
id: actionId,
|
|
1227
|
-
release: {
|
|
1228
|
-
id: releaseId,
|
|
1229
|
-
releasedAt: {
|
|
1230
|
-
$null: true
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
},
|
|
1234
|
-
data: {
|
|
1235
|
-
...update,
|
|
1236
|
-
isEntryValid: actionStatus
|
|
1237
|
-
}
|
|
1238
|
-
});
|
|
1239
|
-
getService('release', {
|
|
1240
|
-
strapi
|
|
1241
|
-
}).updateReleaseStatus(releaseId);
|
|
1242
|
-
return updatedAction;
|
|
1243
|
-
},
|
|
1244
|
-
async delete (actionId, releaseId) {
|
|
1245
|
-
const deletedAction = await strapi.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1246
|
-
where: {
|
|
1247
|
-
id: actionId,
|
|
1248
|
-
release: {
|
|
1249
|
-
id: releaseId,
|
|
1250
|
-
releasedAt: {
|
|
1251
|
-
$null: true
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
});
|
|
1256
|
-
if (!deletedAction) {
|
|
1257
|
-
throw new errors.NotFoundError(`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`);
|
|
1258
|
-
}
|
|
1259
|
-
getService('release', {
|
|
1260
|
-
strapi
|
|
1261
|
-
}).updateReleaseStatus(releaseId);
|
|
1262
|
-
return deletedAction;
|
|
939
|
+
action.entryDocumentId = document.documentId;
|
|
940
|
+
}
|
|
941
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
942
|
+
if (!release2) {
|
|
943
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
944
|
+
}
|
|
945
|
+
if (release2.releasedAt) {
|
|
946
|
+
throw new errors.ValidationError("Release already published");
|
|
947
|
+
}
|
|
948
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
949
|
+
{
|
|
950
|
+
contentType: action.contentType,
|
|
951
|
+
documentId: action.entryDocumentId,
|
|
952
|
+
locale: action.locale
|
|
1263
953
|
},
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
where: {
|
|
1267
|
-
contentType: {
|
|
1268
|
-
$in: contentTypeUids
|
|
1269
|
-
},
|
|
1270
|
-
// We only want to validate actions that are going to be published
|
|
1271
|
-
type: 'publish',
|
|
1272
|
-
release: {
|
|
1273
|
-
releasedAt: {
|
|
1274
|
-
$null: true
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
},
|
|
1278
|
-
populate: {
|
|
1279
|
-
release: true
|
|
1280
|
-
}
|
|
1281
|
-
});
|
|
1282
|
-
const releasesUpdated = [];
|
|
1283
|
-
await async.map(actions, async (action)=>{
|
|
1284
|
-
const isValid = await getDraftEntryValidStatus({
|
|
1285
|
-
contentType: action.contentType,
|
|
1286
|
-
documentId: action.entryDocumentId,
|
|
1287
|
-
locale: action.locale
|
|
1288
|
-
}, {
|
|
1289
|
-
strapi
|
|
1290
|
-
});
|
|
1291
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1292
|
-
where: {
|
|
1293
|
-
id: action.id
|
|
1294
|
-
},
|
|
1295
|
-
data: {
|
|
1296
|
-
isEntryValid: isValid
|
|
1297
|
-
}
|
|
1298
|
-
});
|
|
1299
|
-
if (!releasesUpdated.includes(action.release.id)) {
|
|
1300
|
-
releasesUpdated.push(action.release.id);
|
|
1301
|
-
}
|
|
1302
|
-
return {
|
|
1303
|
-
id: action.id,
|
|
1304
|
-
isEntryValid: isValid
|
|
1305
|
-
};
|
|
1306
|
-
});
|
|
1307
|
-
if (releasesUpdated.length > 0) {
|
|
1308
|
-
await async.map(releasesUpdated, async (releaseId)=>{
|
|
1309
|
-
await getService('release', {
|
|
1310
|
-
strapi
|
|
1311
|
-
}).updateReleaseStatus(releaseId);
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
954
|
+
{
|
|
955
|
+
strapi: strapi2
|
|
1314
956
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
this.name = 'AlreadyOnReleaseError';
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
const createReleaseValidationService = ({ strapi })=>({
|
|
1325
|
-
async validateUniqueEntry (releaseId, releaseActionArgs) {
|
|
1326
|
-
/**
|
|
1327
|
-
* Asserting the type, otherwise TS complains: 'release.actions' is of type 'unknown', even though the types come through for non-populated fields...
|
|
1328
|
-
* Possibly related to the comment on GetValues: https://github.com/strapi/strapi/blob/main/packages/core/types/src/modules/entity-service/result.ts
|
|
1329
|
-
*/ const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({
|
|
1330
|
-
where: {
|
|
1331
|
-
id: releaseId
|
|
1332
|
-
},
|
|
1333
|
-
populate: {
|
|
1334
|
-
actions: true
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1337
|
-
if (!release) {
|
|
1338
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1339
|
-
}
|
|
1340
|
-
const isEntryInRelease = release.actions.some((action)=>action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true));
|
|
1341
|
-
if (isEntryInRelease) {
|
|
1342
|
-
throw new AlreadyOnReleaseError(`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ''} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`);
|
|
1343
|
-
}
|
|
957
|
+
) : true;
|
|
958
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
959
|
+
data: {
|
|
960
|
+
...action,
|
|
961
|
+
release: release2.id,
|
|
962
|
+
isEntryValid: actionStatus
|
|
1344
963
|
},
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
964
|
+
populate: { release: { select: ["id"] } }
|
|
965
|
+
});
|
|
966
|
+
if (!disableUpdateReleaseStatus) {
|
|
967
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
968
|
+
}
|
|
969
|
+
return releaseAction2;
|
|
970
|
+
},
|
|
971
|
+
async findPage(releaseId, query) {
|
|
972
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
973
|
+
where: { id: releaseId },
|
|
974
|
+
select: ["id"]
|
|
975
|
+
});
|
|
976
|
+
if (!release2) {
|
|
977
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
978
|
+
}
|
|
979
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
980
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
981
|
+
...dbQuery,
|
|
982
|
+
where: {
|
|
983
|
+
release: releaseId
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
987
|
+
const actionsWithEntry = await async.map(actions, async (action) => {
|
|
988
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
989
|
+
const entry = await getEntry(
|
|
990
|
+
{
|
|
991
|
+
contentType: action.contentType,
|
|
992
|
+
documentId: action.entryDocumentId,
|
|
993
|
+
locale: action.locale,
|
|
994
|
+
populate,
|
|
995
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
996
|
+
},
|
|
997
|
+
{ strapi: strapi2 }
|
|
998
|
+
);
|
|
999
|
+
return {
|
|
1000
|
+
...action,
|
|
1001
|
+
entry,
|
|
1002
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
1003
|
+
};
|
|
1004
|
+
});
|
|
1005
|
+
return {
|
|
1006
|
+
results: actionsWithEntry,
|
|
1007
|
+
pagination
|
|
1008
|
+
};
|
|
1009
|
+
},
|
|
1010
|
+
async groupActions(actions, groupBy) {
|
|
1011
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1012
|
+
if (!acc.includes(action.contentType)) {
|
|
1013
|
+
acc.push(action.contentType);
|
|
1014
|
+
}
|
|
1015
|
+
return acc;
|
|
1016
|
+
}, []);
|
|
1017
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1018
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
1019
|
+
const formattedData = actions.map((action) => {
|
|
1020
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
1021
|
+
return {
|
|
1022
|
+
...action,
|
|
1023
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
1024
|
+
contentType: {
|
|
1025
|
+
displayName,
|
|
1026
|
+
mainFieldValue: action.entry[mainField],
|
|
1027
|
+
uid: action.contentType
|
|
1028
|
+
}
|
|
1029
|
+
};
|
|
1030
|
+
});
|
|
1031
|
+
const groupName = getGroupName(groupBy);
|
|
1032
|
+
return _.groupBy(groupName)(formattedData);
|
|
1033
|
+
},
|
|
1034
|
+
async getContentTypeModelsFromActions(actions) {
|
|
1035
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1036
|
+
if (!acc.includes(action.contentType)) {
|
|
1037
|
+
acc.push(action.contentType);
|
|
1038
|
+
}
|
|
1039
|
+
return acc;
|
|
1040
|
+
}, []);
|
|
1041
|
+
const workflowsService = strapi2.plugin("review-workflows").service("workflows");
|
|
1042
|
+
const contentTypeModelsMap = await async.reduce(contentTypeUids)(
|
|
1043
|
+
async (accPromise, contentTypeUid) => {
|
|
1044
|
+
const acc = await accPromise;
|
|
1045
|
+
const contentTypeModel = strapi2.getModel(contentTypeUid);
|
|
1046
|
+
const workflow = await workflowsService.getAssignedWorkflow(contentTypeUid, {
|
|
1047
|
+
populate: "stageRequiredToPublish"
|
|
1048
|
+
});
|
|
1049
|
+
acc[contentTypeUid] = {
|
|
1050
|
+
...contentTypeModel,
|
|
1051
|
+
hasReviewWorkflow: !!workflow,
|
|
1052
|
+
stageRequiredToPublish: workflow?.stageRequiredToPublish
|
|
1053
|
+
};
|
|
1054
|
+
return acc;
|
|
1356
1055
|
},
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1056
|
+
{}
|
|
1057
|
+
);
|
|
1058
|
+
return contentTypeModelsMap;
|
|
1059
|
+
},
|
|
1060
|
+
async countActions(query) {
|
|
1061
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1062
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
1063
|
+
},
|
|
1064
|
+
async update(actionId, releaseId, update) {
|
|
1065
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1066
|
+
where: {
|
|
1067
|
+
id: actionId,
|
|
1068
|
+
release: {
|
|
1069
|
+
id: releaseId,
|
|
1070
|
+
releasedAt: {
|
|
1071
|
+
$null: true
|
|
1371
1072
|
}
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
if (!action) {
|
|
1077
|
+
throw new errors.NotFoundError(
|
|
1078
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
|
|
1082
|
+
{
|
|
1083
|
+
contentType: action.contentType,
|
|
1084
|
+
documentId: action.entryDocumentId,
|
|
1085
|
+
locale: action.locale
|
|
1372
1086
|
},
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
});
|
|
1387
|
-
const isNameUnique = pendingReleases.length === 0;
|
|
1388
|
-
if (!isNameUnique) {
|
|
1389
|
-
throw new errors.ValidationError(`Release with name ${name} already exists`);
|
|
1087
|
+
{
|
|
1088
|
+
strapi: strapi2
|
|
1089
|
+
}
|
|
1090
|
+
) : true;
|
|
1091
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1092
|
+
where: {
|
|
1093
|
+
id: actionId,
|
|
1094
|
+
release: {
|
|
1095
|
+
id: releaseId,
|
|
1096
|
+
releasedAt: {
|
|
1097
|
+
$null: true
|
|
1390
1098
|
}
|
|
1099
|
+
}
|
|
1391
1100
|
},
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
}
|
|
1101
|
+
data: {
|
|
1102
|
+
...update,
|
|
1103
|
+
isEntryValid: actionStatus
|
|
1396
1104
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
if (!release) {
|
|
1410
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1411
|
-
}
|
|
1412
|
-
const job = scheduleJob(scheduleDate, async ()=>{
|
|
1413
|
-
try {
|
|
1414
|
-
await getService('release', {
|
|
1415
|
-
strapi
|
|
1416
|
-
}).publish(releaseId);
|
|
1417
|
-
// @TODO: Trigger webhook with success message
|
|
1418
|
-
} catch (error) {
|
|
1419
|
-
// @TODO: Trigger webhook with error message
|
|
1420
|
-
}
|
|
1421
|
-
this.cancel(releaseId);
|
|
1422
|
-
});
|
|
1423
|
-
if (scheduledJobs.has(releaseId)) {
|
|
1424
|
-
this.cancel(releaseId);
|
|
1105
|
+
});
|
|
1106
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1107
|
+
return updatedAction;
|
|
1108
|
+
},
|
|
1109
|
+
async delete(actionId, releaseId) {
|
|
1110
|
+
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1111
|
+
where: {
|
|
1112
|
+
id: actionId,
|
|
1113
|
+
release: {
|
|
1114
|
+
id: releaseId,
|
|
1115
|
+
releasedAt: {
|
|
1116
|
+
$null: true
|
|
1425
1117
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
if (!deletedAction) {
|
|
1122
|
+
throw new errors.NotFoundError(
|
|
1123
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1127
|
+
return deletedAction;
|
|
1128
|
+
},
|
|
1129
|
+
async validateActionsByContentTypes(contentTypeUids) {
|
|
1130
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
1131
|
+
where: {
|
|
1132
|
+
contentType: {
|
|
1133
|
+
$in: contentTypeUids
|
|
1134
|
+
},
|
|
1135
|
+
// We only want to validate actions that are going to be published
|
|
1136
|
+
type: "publish",
|
|
1137
|
+
release: {
|
|
1138
|
+
releasedAt: {
|
|
1139
|
+
$null: true
|
|
1433
1140
|
}
|
|
1434
|
-
|
|
1141
|
+
}
|
|
1435
1142
|
},
|
|
1436
|
-
|
|
1437
|
-
|
|
1143
|
+
populate: { release: true }
|
|
1144
|
+
});
|
|
1145
|
+
const releasesUpdated = [];
|
|
1146
|
+
await async.map(actions, async (action) => {
|
|
1147
|
+
const isValid = await getDraftEntryValidStatus(
|
|
1148
|
+
{
|
|
1149
|
+
contentType: action.contentType,
|
|
1150
|
+
documentId: action.entryDocumentId,
|
|
1151
|
+
locale: action.locale
|
|
1152
|
+
},
|
|
1153
|
+
{ strapi: strapi2 }
|
|
1154
|
+
);
|
|
1155
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1156
|
+
where: {
|
|
1157
|
+
id: action.id
|
|
1158
|
+
},
|
|
1159
|
+
data: {
|
|
1160
|
+
isEntryValid: isValid
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
if (!releasesUpdated.includes(action.release.id)) {
|
|
1164
|
+
releasesUpdated.push(action.release.id);
|
|
1165
|
+
}
|
|
1166
|
+
return {
|
|
1167
|
+
id: action.id,
|
|
1168
|
+
isEntryValid: isValid
|
|
1169
|
+
};
|
|
1170
|
+
});
|
|
1171
|
+
if (releasesUpdated.length > 0) {
|
|
1172
|
+
await async.map(releasesUpdated, async (releaseId) => {
|
|
1173
|
+
await getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
};
|
|
1179
|
+
class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
1180
|
+
constructor(message) {
|
|
1181
|
+
super(message);
|
|
1182
|
+
this.name = "AlreadyOnReleaseError";
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1186
|
+
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1187
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1188
|
+
where: {
|
|
1189
|
+
id: releaseId
|
|
1190
|
+
},
|
|
1191
|
+
populate: {
|
|
1192
|
+
actions: true
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
if (!release2) {
|
|
1196
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1197
|
+
}
|
|
1198
|
+
const isEntryInRelease = release2.actions.some(
|
|
1199
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1200
|
+
);
|
|
1201
|
+
if (isEntryInRelease) {
|
|
1202
|
+
throw new AlreadyOnReleaseError(
|
|
1203
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
},
|
|
1207
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1208
|
+
const contentType = strapi2.contentType(contentTypeUid);
|
|
1209
|
+
if (!contentType) {
|
|
1210
|
+
throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1211
|
+
}
|
|
1212
|
+
if (!contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
1213
|
+
throw new errors.ValidationError(
|
|
1214
|
+
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1218
|
+
throw new errors.ValidationError("Document id is required for collection type");
|
|
1219
|
+
}
|
|
1220
|
+
},
|
|
1221
|
+
async validatePendingReleasesLimit() {
|
|
1222
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1223
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1224
|
+
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1225
|
+
filters: {
|
|
1226
|
+
releasedAt: {
|
|
1227
|
+
$null: true
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
});
|
|
1231
|
+
if (pendingReleasesCount >= maximumPendingReleases) {
|
|
1232
|
+
throw new errors.ValidationError("You have reached the maximum number of pending releases");
|
|
1233
|
+
}
|
|
1234
|
+
},
|
|
1235
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
1236
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1237
|
+
where: {
|
|
1238
|
+
releasedAt: {
|
|
1239
|
+
$null: true
|
|
1438
1240
|
},
|
|
1439
|
-
|
|
1241
|
+
name,
|
|
1242
|
+
...id && { id: { $ne: id } }
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
const isNameUnique = pendingReleases.length === 0;
|
|
1246
|
+
if (!isNameUnique) {
|
|
1247
|
+
throw new errors.ValidationError(`Release with name ${name} already exists`);
|
|
1248
|
+
}
|
|
1249
|
+
},
|
|
1250
|
+
async validateScheduledAtIsLaterThanNow(scheduledAt) {
|
|
1251
|
+
if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
|
|
1252
|
+
throw new errors.ValidationError("Scheduled at must be later than now");
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
});
|
|
1256
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
1257
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
1258
|
+
return {
|
|
1259
|
+
async set(releaseId, scheduleDate) {
|
|
1260
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
|
|
1261
|
+
if (!release2) {
|
|
1262
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1263
|
+
}
|
|
1264
|
+
const job = scheduleJob(scheduleDate, async () => {
|
|
1265
|
+
try {
|
|
1266
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1267
|
+
} catch (error) {
|
|
1268
|
+
}
|
|
1269
|
+
this.cancel(releaseId);
|
|
1270
|
+
});
|
|
1271
|
+
if (scheduledJobs.has(releaseId)) {
|
|
1272
|
+
this.cancel(releaseId);
|
|
1273
|
+
}
|
|
1274
|
+
scheduledJobs.set(releaseId, job);
|
|
1275
|
+
return scheduledJobs;
|
|
1276
|
+
},
|
|
1277
|
+
cancel(releaseId) {
|
|
1278
|
+
if (scheduledJobs.has(releaseId)) {
|
|
1279
|
+
scheduledJobs.get(releaseId).cancel();
|
|
1280
|
+
scheduledJobs.delete(releaseId);
|
|
1281
|
+
}
|
|
1282
|
+
return scheduledJobs;
|
|
1283
|
+
},
|
|
1284
|
+
getAll() {
|
|
1285
|
+
return scheduledJobs;
|
|
1286
|
+
},
|
|
1287
|
+
/**
|
|
1440
1288
|
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
1441
1289
|
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
1442
1290
|
* This also could be used to sync different Strapi instances in case of a cluster
|
|
1443
|
-
*/
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
});
|
|
1452
|
-
for (const release of releases){
|
|
1453
|
-
this.set(release.id, release.scheduledAt);
|
|
1454
|
-
}
|
|
1455
|
-
return scheduledJobs;
|
|
1291
|
+
*/
|
|
1292
|
+
async syncFromDatabase() {
|
|
1293
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1294
|
+
where: {
|
|
1295
|
+
scheduledAt: {
|
|
1296
|
+
$gte: /* @__PURE__ */ new Date()
|
|
1297
|
+
},
|
|
1298
|
+
releasedAt: null
|
|
1456
1299
|
}
|
|
1457
|
-
|
|
1300
|
+
});
|
|
1301
|
+
for (const release2 of releases) {
|
|
1302
|
+
this.set(release2.id, release2.scheduledAt);
|
|
1303
|
+
}
|
|
1304
|
+
return scheduledJobs;
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1458
1307
|
};
|
|
1459
|
-
|
|
1460
1308
|
const DEFAULT_SETTINGS = {
|
|
1461
|
-
|
|
1309
|
+
defaultTimezone: null
|
|
1462
1310
|
};
|
|
1463
|
-
const createSettingsService = ({ strapi })=>{
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
key: 'settings'
|
|
1481
|
-
});
|
|
1482
|
-
return {
|
|
1483
|
-
...DEFAULT_SETTINGS,
|
|
1484
|
-
...settings || {}
|
|
1485
|
-
};
|
|
1486
|
-
}
|
|
1487
|
-
};
|
|
1311
|
+
const createSettingsService = ({ strapi: strapi2 }) => {
|
|
1312
|
+
const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
|
|
1313
|
+
return {
|
|
1314
|
+
async update({ settings: settings2 }) {
|
|
1315
|
+
const store = await getStore();
|
|
1316
|
+
store.set({ key: "settings", value: settings2 });
|
|
1317
|
+
return settings2;
|
|
1318
|
+
},
|
|
1319
|
+
async find() {
|
|
1320
|
+
const store = await getStore();
|
|
1321
|
+
const settings2 = await store.get({ key: "settings" });
|
|
1322
|
+
return {
|
|
1323
|
+
...DEFAULT_SETTINGS,
|
|
1324
|
+
...settings2 || {}
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
};
|
|
1488
1328
|
};
|
|
1489
|
-
|
|
1490
1329
|
const services = {
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1330
|
+
release: createReleaseService,
|
|
1331
|
+
"release-action": createReleaseActionService,
|
|
1332
|
+
"release-validation": createReleaseValidationService,
|
|
1333
|
+
scheduling: createSchedulingService,
|
|
1334
|
+
settings: createSettingsService
|
|
1496
1335
|
};
|
|
1497
|
-
|
|
1498
1336
|
const RELEASE_SCHEMA = yup$1.object().shape({
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1337
|
+
name: yup$1.string().trim().required(),
|
|
1338
|
+
scheduledAt: yup$1.string().nullable(),
|
|
1339
|
+
timezone: yup$1.string().when("scheduledAt", {
|
|
1340
|
+
is: (value) => value !== null && value !== void 0,
|
|
1341
|
+
then: yup$1.string().required(),
|
|
1342
|
+
otherwise: yup$1.string().nullable()
|
|
1343
|
+
})
|
|
1506
1344
|
}).required().noUnknown();
|
|
1507
1345
|
const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = yup$1.object().shape({
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1346
|
+
contentType: yup$1.string().required(),
|
|
1347
|
+
entryDocumentId: yup$1.string().nullable(),
|
|
1348
|
+
hasEntryAttached: yup$1.string().nullable(),
|
|
1349
|
+
locale: yup$1.string().nullable()
|
|
1512
1350
|
}).required().noUnknown();
|
|
1513
1351
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
1514
|
-
const validatefindByDocumentAttachedParams = validateYupSchema(
|
|
1515
|
-
|
|
1352
|
+
const validatefindByDocumentAttachedParams = validateYupSchema(
|
|
1353
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1354
|
+
);
|
|
1516
1355
|
const releaseController = {
|
|
1517
|
-
|
|
1356
|
+
/**
|
|
1518
1357
|
* Find releases based on documents attached or not to the release.
|
|
1519
1358
|
* If `hasEntryAttached` is true, it will return all releases that have the entry attached.
|
|
1520
1359
|
* If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
|
|
1521
|
-
*/
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1360
|
+
*/
|
|
1361
|
+
async findByDocumentAttached(ctx) {
|
|
1362
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1363
|
+
ability: ctx.state.userAbility,
|
|
1364
|
+
model: RELEASE_MODEL_UID
|
|
1365
|
+
});
|
|
1366
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1367
|
+
const releaseService = getService("release", { strapi });
|
|
1368
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1369
|
+
await validatefindByDocumentAttachedParams(query);
|
|
1370
|
+
const model = strapi.getModel(query.contentType);
|
|
1371
|
+
if (model.kind && model.kind === "singleType") {
|
|
1372
|
+
const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
1373
|
+
if (!document) {
|
|
1374
|
+
throw new errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
|
|
1375
|
+
}
|
|
1376
|
+
query.entryDocumentId = document.documentId;
|
|
1377
|
+
}
|
|
1378
|
+
const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
|
|
1379
|
+
const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
|
|
1380
|
+
if (isEntryAttached) {
|
|
1381
|
+
const releases = await releaseService.findMany({
|
|
1382
|
+
where: {
|
|
1383
|
+
releasedAt: null,
|
|
1384
|
+
actions: {
|
|
1385
|
+
contentType,
|
|
1386
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1387
|
+
locale: locale ?? null
|
|
1388
|
+
}
|
|
1389
|
+
},
|
|
1390
|
+
populate: {
|
|
1391
|
+
actions: {
|
|
1392
|
+
fields: ["type"],
|
|
1393
|
+
filters: {
|
|
1394
|
+
contentType,
|
|
1395
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1396
|
+
locale: locale ?? null
|
|
1542
1397
|
}
|
|
1543
|
-
|
|
1398
|
+
}
|
|
1544
1399
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
},
|
|
1557
|
-
populate: {
|
|
1558
|
-
actions: {
|
|
1559
|
-
fields: [
|
|
1560
|
-
'type'
|
|
1561
|
-
],
|
|
1562
|
-
filters: {
|
|
1563
|
-
contentType,
|
|
1564
|
-
entryDocumentId: entryDocumentId ?? null,
|
|
1565
|
-
locale: locale ?? null
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
});
|
|
1570
|
-
ctx.body = {
|
|
1571
|
-
data: releases
|
|
1572
|
-
};
|
|
1573
|
-
} else {
|
|
1574
|
-
const relatedReleases = await releaseService.findMany({
|
|
1575
|
-
where: {
|
|
1576
|
-
releasedAt: null,
|
|
1577
|
-
actions: {
|
|
1578
|
-
contentType,
|
|
1579
|
-
entryDocumentId: entryDocumentId ?? null,
|
|
1580
|
-
locale: locale ?? null
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
});
|
|
1584
|
-
const releases = await releaseService.findMany({
|
|
1585
|
-
where: {
|
|
1586
|
-
$or: [
|
|
1587
|
-
{
|
|
1588
|
-
id: {
|
|
1589
|
-
$notIn: relatedReleases.map((release)=>release.id)
|
|
1590
|
-
}
|
|
1591
|
-
},
|
|
1592
|
-
{
|
|
1593
|
-
actions: null
|
|
1594
|
-
}
|
|
1595
|
-
],
|
|
1596
|
-
releasedAt: null
|
|
1597
|
-
}
|
|
1598
|
-
});
|
|
1599
|
-
ctx.body = {
|
|
1600
|
-
data: releases
|
|
1601
|
-
};
|
|
1400
|
+
});
|
|
1401
|
+
ctx.body = { data: releases };
|
|
1402
|
+
} else {
|
|
1403
|
+
const relatedReleases = await releaseService.findMany({
|
|
1404
|
+
where: {
|
|
1405
|
+
releasedAt: null,
|
|
1406
|
+
actions: {
|
|
1407
|
+
contentType,
|
|
1408
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1409
|
+
locale: locale ?? null
|
|
1410
|
+
}
|
|
1602
1411
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
const { results, pagination } = await releaseService.findPage(query);
|
|
1615
|
-
const data = results.map((release)=>{
|
|
1616
|
-
const { actions, ...releaseData } = release;
|
|
1617
|
-
return {
|
|
1618
|
-
...releaseData,
|
|
1619
|
-
actions: {
|
|
1620
|
-
meta: {
|
|
1621
|
-
count: actions.count
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
};
|
|
1625
|
-
});
|
|
1626
|
-
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1627
|
-
where: {
|
|
1628
|
-
releasedAt: null
|
|
1629
|
-
}
|
|
1630
|
-
});
|
|
1631
|
-
ctx.body = {
|
|
1632
|
-
data,
|
|
1633
|
-
meta: {
|
|
1634
|
-
pagination,
|
|
1635
|
-
pendingReleasesCount
|
|
1412
|
+
});
|
|
1413
|
+
const releases = await releaseService.findMany({
|
|
1414
|
+
where: {
|
|
1415
|
+
$or: [
|
|
1416
|
+
{
|
|
1417
|
+
id: {
|
|
1418
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
actions: null
|
|
1636
1423
|
}
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
async findOne (ctx) {
|
|
1640
|
-
const id = ctx.params.id;
|
|
1641
|
-
const releaseService = getService('release', {
|
|
1642
|
-
strapi
|
|
1643
|
-
});
|
|
1644
|
-
const releaseActionService = getService('release-action', {
|
|
1645
|
-
strapi
|
|
1646
|
-
});
|
|
1647
|
-
const release = await releaseService.findOne(id, {
|
|
1648
|
-
populate: [
|
|
1649
|
-
'createdBy'
|
|
1650
|
-
]
|
|
1651
|
-
});
|
|
1652
|
-
if (!release) {
|
|
1653
|
-
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1424
|
+
],
|
|
1425
|
+
releasedAt: null
|
|
1654
1426
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
},
|
|
1677
|
-
async mapEntriesToReleases (ctx) {
|
|
1678
|
-
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1679
|
-
if (!contentTypeUid || !documentIds) {
|
|
1680
|
-
throw new errors.ValidationError('Missing required query parameters');
|
|
1427
|
+
});
|
|
1428
|
+
ctx.body = { data: releases };
|
|
1429
|
+
}
|
|
1430
|
+
},
|
|
1431
|
+
async findPage(ctx) {
|
|
1432
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1433
|
+
ability: ctx.state.userAbility,
|
|
1434
|
+
model: RELEASE_MODEL_UID
|
|
1435
|
+
});
|
|
1436
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1437
|
+
const releaseService = getService("release", { strapi });
|
|
1438
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1439
|
+
const { results, pagination } = await releaseService.findPage(query);
|
|
1440
|
+
const data = results.map((release2) => {
|
|
1441
|
+
const { actions, ...releaseData } = release2;
|
|
1442
|
+
return {
|
|
1443
|
+
...releaseData,
|
|
1444
|
+
actions: {
|
|
1445
|
+
meta: {
|
|
1446
|
+
count: actions.count
|
|
1447
|
+
}
|
|
1681
1448
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
return acc;
|
|
1723
|
-
}, {});
|
|
1724
|
-
ctx.body = {
|
|
1725
|
-
data: mappedEntriesInReleases
|
|
1726
|
-
};
|
|
1727
|
-
},
|
|
1728
|
-
async create (ctx) {
|
|
1729
|
-
const user = ctx.state.user;
|
|
1730
|
-
const releaseArgs = ctx.request.body;
|
|
1731
|
-
await validateRelease(releaseArgs);
|
|
1732
|
-
const releaseService = getService('release', {
|
|
1733
|
-
strapi
|
|
1734
|
-
});
|
|
1735
|
-
const release = await releaseService.create(releaseArgs, {
|
|
1736
|
-
user
|
|
1737
|
-
});
|
|
1738
|
-
const permissionsManager = strapi.service('admin::permission').createPermissionsManager({
|
|
1739
|
-
ability: ctx.state.userAbility,
|
|
1740
|
-
model: RELEASE_MODEL_UID
|
|
1741
|
-
});
|
|
1742
|
-
ctx.created({
|
|
1743
|
-
data: await permissionsManager.sanitizeOutput(release)
|
|
1744
|
-
});
|
|
1745
|
-
},
|
|
1746
|
-
async update (ctx) {
|
|
1747
|
-
const user = ctx.state.user;
|
|
1748
|
-
const releaseArgs = ctx.request.body;
|
|
1749
|
-
const id = ctx.params.id;
|
|
1750
|
-
await validateRelease(releaseArgs);
|
|
1751
|
-
const releaseService = getService('release', {
|
|
1752
|
-
strapi
|
|
1753
|
-
});
|
|
1754
|
-
const release = await releaseService.update(id, releaseArgs, {
|
|
1755
|
-
user
|
|
1756
|
-
});
|
|
1757
|
-
const permissionsManager = strapi.service('admin::permission').createPermissionsManager({
|
|
1758
|
-
ability: ctx.state.userAbility,
|
|
1759
|
-
model: RELEASE_MODEL_UID
|
|
1760
|
-
});
|
|
1761
|
-
ctx.body = {
|
|
1762
|
-
data: await permissionsManager.sanitizeOutput(release)
|
|
1763
|
-
};
|
|
1764
|
-
},
|
|
1765
|
-
async delete (ctx) {
|
|
1766
|
-
const id = ctx.params.id;
|
|
1767
|
-
const releaseService = getService('release', {
|
|
1768
|
-
strapi
|
|
1769
|
-
});
|
|
1770
|
-
const release = await releaseService.delete(id);
|
|
1771
|
-
ctx.body = {
|
|
1772
|
-
data: release
|
|
1773
|
-
};
|
|
1774
|
-
},
|
|
1775
|
-
async publish (ctx) {
|
|
1776
|
-
const id = ctx.params.id;
|
|
1777
|
-
const releaseService = getService('release', {
|
|
1778
|
-
strapi
|
|
1779
|
-
});
|
|
1780
|
-
const releaseActionService = getService('release-action', {
|
|
1781
|
-
strapi
|
|
1782
|
-
});
|
|
1783
|
-
const release = await releaseService.publish(id);
|
|
1784
|
-
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1785
|
-
releaseActionService.countActions({
|
|
1786
|
-
filters: {
|
|
1787
|
-
release: id,
|
|
1788
|
-
type: 'publish'
|
|
1789
|
-
}
|
|
1790
|
-
}),
|
|
1791
|
-
releaseActionService.countActions({
|
|
1792
|
-
filters: {
|
|
1793
|
-
release: id,
|
|
1794
|
-
type: 'unpublish'
|
|
1795
|
-
}
|
|
1796
|
-
})
|
|
1797
|
-
]);
|
|
1798
|
-
ctx.body = {
|
|
1799
|
-
data: release,
|
|
1800
|
-
meta: {
|
|
1801
|
-
totalEntries: countPublishActions + countUnpublishActions,
|
|
1802
|
-
totalPublishedEntries: countPublishActions,
|
|
1803
|
-
totalUnpublishedEntries: countUnpublishActions
|
|
1804
|
-
}
|
|
1805
|
-
};
|
|
1449
|
+
};
|
|
1450
|
+
});
|
|
1451
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1452
|
+
where: {
|
|
1453
|
+
releasedAt: null
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
1457
|
+
},
|
|
1458
|
+
async findOne(ctx) {
|
|
1459
|
+
const id = ctx.params.id;
|
|
1460
|
+
const releaseService = getService("release", { strapi });
|
|
1461
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1462
|
+
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1463
|
+
if (!release2) {
|
|
1464
|
+
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1465
|
+
}
|
|
1466
|
+
const count = await releaseActionService.countActions({
|
|
1467
|
+
filters: {
|
|
1468
|
+
release: id
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
const sanitizedRelease = {
|
|
1472
|
+
...release2,
|
|
1473
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1474
|
+
};
|
|
1475
|
+
const data = {
|
|
1476
|
+
...sanitizedRelease,
|
|
1477
|
+
actions: {
|
|
1478
|
+
meta: {
|
|
1479
|
+
count
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
ctx.body = { data };
|
|
1484
|
+
},
|
|
1485
|
+
async mapEntriesToReleases(ctx) {
|
|
1486
|
+
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1487
|
+
if (!contentTypeUid || !documentIds) {
|
|
1488
|
+
throw new errors.ValidationError("Missing required query parameters");
|
|
1806
1489
|
}
|
|
1490
|
+
const releaseService = getService("release", { strapi });
|
|
1491
|
+
const releasesWithActions = await releaseService.findMany({
|
|
1492
|
+
where: {
|
|
1493
|
+
releasedAt: null,
|
|
1494
|
+
actions: {
|
|
1495
|
+
contentType: contentTypeUid,
|
|
1496
|
+
entryDocumentId: {
|
|
1497
|
+
$in: documentIds
|
|
1498
|
+
},
|
|
1499
|
+
locale
|
|
1500
|
+
}
|
|
1501
|
+
},
|
|
1502
|
+
populate: {
|
|
1503
|
+
actions: true
|
|
1504
|
+
}
|
|
1505
|
+
});
|
|
1506
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1507
|
+
(acc, release2) => {
|
|
1508
|
+
release2.actions.forEach((action) => {
|
|
1509
|
+
if (action.contentType !== contentTypeUid) {
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
if (locale && action.locale !== locale) {
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
if (!acc[action.entryDocumentId]) {
|
|
1516
|
+
acc[action.entryDocumentId] = [{ id: release2.id, name: release2.name }];
|
|
1517
|
+
} else {
|
|
1518
|
+
acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
|
|
1519
|
+
}
|
|
1520
|
+
});
|
|
1521
|
+
return acc;
|
|
1522
|
+
},
|
|
1523
|
+
{}
|
|
1524
|
+
);
|
|
1525
|
+
ctx.body = {
|
|
1526
|
+
data: mappedEntriesInReleases
|
|
1527
|
+
};
|
|
1528
|
+
},
|
|
1529
|
+
async create(ctx) {
|
|
1530
|
+
const user = ctx.state.user;
|
|
1531
|
+
const releaseArgs = ctx.request.body;
|
|
1532
|
+
await validateRelease(releaseArgs);
|
|
1533
|
+
const releaseService = getService("release", { strapi });
|
|
1534
|
+
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1535
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1536
|
+
ability: ctx.state.userAbility,
|
|
1537
|
+
model: RELEASE_MODEL_UID
|
|
1538
|
+
});
|
|
1539
|
+
ctx.created({
|
|
1540
|
+
data: await permissionsManager.sanitizeOutput(release2)
|
|
1541
|
+
});
|
|
1542
|
+
},
|
|
1543
|
+
async update(ctx) {
|
|
1544
|
+
const user = ctx.state.user;
|
|
1545
|
+
const releaseArgs = ctx.request.body;
|
|
1546
|
+
const id = ctx.params.id;
|
|
1547
|
+
await validateRelease(releaseArgs);
|
|
1548
|
+
const releaseService = getService("release", { strapi });
|
|
1549
|
+
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1550
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1551
|
+
ability: ctx.state.userAbility,
|
|
1552
|
+
model: RELEASE_MODEL_UID
|
|
1553
|
+
});
|
|
1554
|
+
ctx.body = {
|
|
1555
|
+
data: await permissionsManager.sanitizeOutput(release2)
|
|
1556
|
+
};
|
|
1557
|
+
},
|
|
1558
|
+
async delete(ctx) {
|
|
1559
|
+
const id = ctx.params.id;
|
|
1560
|
+
const releaseService = getService("release", { strapi });
|
|
1561
|
+
const release2 = await releaseService.delete(id);
|
|
1562
|
+
ctx.body = {
|
|
1563
|
+
data: release2
|
|
1564
|
+
};
|
|
1565
|
+
},
|
|
1566
|
+
async publish(ctx) {
|
|
1567
|
+
const id = ctx.params.id;
|
|
1568
|
+
const releaseService = getService("release", { strapi });
|
|
1569
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1570
|
+
const release2 = await releaseService.publish(id);
|
|
1571
|
+
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1572
|
+
releaseActionService.countActions({
|
|
1573
|
+
filters: {
|
|
1574
|
+
release: id,
|
|
1575
|
+
type: "publish"
|
|
1576
|
+
}
|
|
1577
|
+
}),
|
|
1578
|
+
releaseActionService.countActions({
|
|
1579
|
+
filters: {
|
|
1580
|
+
release: id,
|
|
1581
|
+
type: "unpublish"
|
|
1582
|
+
}
|
|
1583
|
+
})
|
|
1584
|
+
]);
|
|
1585
|
+
ctx.body = {
|
|
1586
|
+
data: release2,
|
|
1587
|
+
meta: {
|
|
1588
|
+
totalEntries: countPublishActions + countUnpublishActions,
|
|
1589
|
+
totalPublishedEntries: countPublishActions,
|
|
1590
|
+
totalUnpublishedEntries: countUnpublishActions
|
|
1591
|
+
}
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1807
1594
|
};
|
|
1808
|
-
|
|
1809
1595
|
const RELEASE_ACTION_SCHEMA = yup$1.object().shape({
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
'publish',
|
|
1815
|
-
'unpublish'
|
|
1816
|
-
]).required()
|
|
1596
|
+
contentType: yup$1.string().required(),
|
|
1597
|
+
entryDocumentId: yup$1.strapiID(),
|
|
1598
|
+
locale: yup$1.string(),
|
|
1599
|
+
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1817
1600
|
});
|
|
1818
1601
|
const RELEASE_ACTION_UPDATE_SCHEMA = yup$1.object().shape({
|
|
1819
|
-
|
|
1820
|
-
'publish',
|
|
1821
|
-
'unpublish'
|
|
1822
|
-
]).required()
|
|
1602
|
+
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1823
1603
|
});
|
|
1824
1604
|
const FIND_MANY_ACTIONS_PARAMS = yup$1.object().shape({
|
|
1825
|
-
|
|
1826
|
-
'action',
|
|
1827
|
-
'contentType',
|
|
1828
|
-
'locale'
|
|
1829
|
-
])
|
|
1605
|
+
groupBy: yup$1.string().oneOf(["action", "contentType", "locale"])
|
|
1830
1606
|
});
|
|
1831
1607
|
const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1832
1608
|
const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1833
1609
|
const validateFindManyActionsParams = validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1834
|
-
|
|
1835
1610
|
const releaseActionController = {
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
try {
|
|
1861
|
-
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1862
|
-
disableUpdateReleaseStatus: true
|
|
1863
|
-
});
|
|
1864
|
-
return action;
|
|
1865
|
-
} catch (error) {
|
|
1866
|
-
// If the entry is already in the release, we don't want to throw an error, so we catch and ignore it
|
|
1867
|
-
if (error instanceof AlreadyOnReleaseError) {
|
|
1868
|
-
return null;
|
|
1869
|
-
}
|
|
1870
|
-
throw error;
|
|
1871
|
-
}
|
|
1872
|
-
}));
|
|
1873
|
-
return releaseActions;
|
|
1874
|
-
});
|
|
1875
|
-
const newReleaseActions = releaseActions.filter((action)=>action !== null);
|
|
1876
|
-
if (newReleaseActions.length > 0) {
|
|
1877
|
-
releaseService.updateReleaseStatus(releaseId);
|
|
1878
|
-
}
|
|
1879
|
-
ctx.created({
|
|
1880
|
-
data: newReleaseActions,
|
|
1881
|
-
meta: {
|
|
1882
|
-
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1883
|
-
totalEntries: releaseActions.length
|
|
1884
|
-
}
|
|
1885
|
-
});
|
|
1886
|
-
},
|
|
1887
|
-
async findMany (ctx) {
|
|
1888
|
-
const releaseId = ctx.params.releaseId;
|
|
1889
|
-
const permissionsManager = strapi.service('admin::permission').createPermissionsManager({
|
|
1890
|
-
ability: ctx.state.userAbility,
|
|
1891
|
-
model: RELEASE_ACTION_MODEL_UID
|
|
1892
|
-
});
|
|
1893
|
-
await validateFindManyActionsParams(ctx.query);
|
|
1894
|
-
if (ctx.query.groupBy) {
|
|
1895
|
-
if (![
|
|
1896
|
-
'action',
|
|
1897
|
-
'contentType',
|
|
1898
|
-
'locale'
|
|
1899
|
-
].includes(ctx.query.groupBy)) {
|
|
1900
|
-
ctx.badRequest('Invalid groupBy parameter');
|
|
1901
|
-
}
|
|
1902
|
-
}
|
|
1903
|
-
ctx.query.sort = ctx.query.groupBy === 'action' ? 'type' : ctx.query.groupBy;
|
|
1904
|
-
delete ctx.query.groupBy;
|
|
1905
|
-
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1906
|
-
const releaseActionService = getService('release-action', {
|
|
1907
|
-
strapi
|
|
1908
|
-
});
|
|
1909
|
-
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1910
|
-
...query
|
|
1911
|
-
});
|
|
1912
|
-
/**
|
|
1913
|
-
* Release actions can be related to entries of different content types.
|
|
1914
|
-
* We need to sanitize the entry output according to that content type.
|
|
1915
|
-
* So, we group the sanitized output function by content type.
|
|
1916
|
-
*/ const contentTypeOutputSanitizers = results.reduce((acc, action)=>{
|
|
1917
|
-
if (acc[action.contentType]) {
|
|
1918
|
-
return acc;
|
|
1919
|
-
}
|
|
1920
|
-
const contentTypePermissionsManager = strapi.service('admin::permission').createPermissionsManager({
|
|
1921
|
-
ability: ctx.state.userAbility,
|
|
1922
|
-
model: action.contentType
|
|
1611
|
+
async create(ctx) {
|
|
1612
|
+
const releaseId = ctx.params.releaseId;
|
|
1613
|
+
const releaseActionArgs = ctx.request.body;
|
|
1614
|
+
await validateReleaseAction(releaseActionArgs);
|
|
1615
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1616
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1617
|
+
ctx.created({
|
|
1618
|
+
data: releaseAction2
|
|
1619
|
+
});
|
|
1620
|
+
},
|
|
1621
|
+
async createMany(ctx) {
|
|
1622
|
+
const releaseId = ctx.params.releaseId;
|
|
1623
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1624
|
+
await Promise.all(
|
|
1625
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1626
|
+
);
|
|
1627
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1628
|
+
const releaseService = getService("release", { strapi });
|
|
1629
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1630
|
+
const releaseActions2 = await Promise.all(
|
|
1631
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1632
|
+
try {
|
|
1633
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1634
|
+
disableUpdateReleaseStatus: true
|
|
1923
1635
|
});
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
ctx.body = {
|
|
1959
|
-
data: updatedAction
|
|
1960
|
-
};
|
|
1961
|
-
},
|
|
1962
|
-
async delete (ctx) {
|
|
1963
|
-
const actionId = ctx.params.actionId;
|
|
1964
|
-
const releaseId = ctx.params.releaseId;
|
|
1965
|
-
const releaseActionService = getService('release-action', {
|
|
1966
|
-
strapi
|
|
1967
|
-
});
|
|
1968
|
-
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1969
|
-
ctx.body = {
|
|
1970
|
-
data: deletedReleaseAction
|
|
1971
|
-
};
|
|
1636
|
+
return action;
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1639
|
+
return null;
|
|
1640
|
+
}
|
|
1641
|
+
throw error;
|
|
1642
|
+
}
|
|
1643
|
+
})
|
|
1644
|
+
);
|
|
1645
|
+
return releaseActions2;
|
|
1646
|
+
});
|
|
1647
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1648
|
+
if (newReleaseActions.length > 0) {
|
|
1649
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1650
|
+
}
|
|
1651
|
+
ctx.created({
|
|
1652
|
+
data: newReleaseActions,
|
|
1653
|
+
meta: {
|
|
1654
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1655
|
+
totalEntries: releaseActions.length
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
},
|
|
1659
|
+
async findMany(ctx) {
|
|
1660
|
+
const releaseId = ctx.params.releaseId;
|
|
1661
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1662
|
+
ability: ctx.state.userAbility,
|
|
1663
|
+
model: RELEASE_ACTION_MODEL_UID
|
|
1664
|
+
});
|
|
1665
|
+
await validateFindManyActionsParams(ctx.query);
|
|
1666
|
+
if (ctx.query.groupBy) {
|
|
1667
|
+
if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
|
|
1668
|
+
ctx.badRequest("Invalid groupBy parameter");
|
|
1669
|
+
}
|
|
1972
1670
|
}
|
|
1671
|
+
ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
|
|
1672
|
+
delete ctx.query.groupBy;
|
|
1673
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1674
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1675
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1676
|
+
...query
|
|
1677
|
+
});
|
|
1678
|
+
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1679
|
+
if (acc[action.contentType]) {
|
|
1680
|
+
return acc;
|
|
1681
|
+
}
|
|
1682
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1683
|
+
ability: ctx.state.userAbility,
|
|
1684
|
+
model: action.contentType
|
|
1685
|
+
});
|
|
1686
|
+
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
1687
|
+
return acc;
|
|
1688
|
+
}, {});
|
|
1689
|
+
const sanitizedResults = await async.map(results, async (action) => ({
|
|
1690
|
+
...action,
|
|
1691
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1692
|
+
}));
|
|
1693
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1694
|
+
const contentTypes2 = await releaseActionService.getContentTypeModelsFromActions(results);
|
|
1695
|
+
const releaseService = getService("release", { strapi });
|
|
1696
|
+
const components = await releaseService.getAllComponents();
|
|
1697
|
+
ctx.body = {
|
|
1698
|
+
data: groupedData,
|
|
1699
|
+
meta: {
|
|
1700
|
+
pagination,
|
|
1701
|
+
contentTypes: contentTypes2,
|
|
1702
|
+
components
|
|
1703
|
+
}
|
|
1704
|
+
};
|
|
1705
|
+
},
|
|
1706
|
+
async update(ctx) {
|
|
1707
|
+
const actionId = ctx.params.actionId;
|
|
1708
|
+
const releaseId = ctx.params.releaseId;
|
|
1709
|
+
const releaseActionUpdateArgs = ctx.request.body;
|
|
1710
|
+
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1711
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1712
|
+
const updatedAction = await releaseActionService.update(
|
|
1713
|
+
actionId,
|
|
1714
|
+
releaseId,
|
|
1715
|
+
releaseActionUpdateArgs
|
|
1716
|
+
);
|
|
1717
|
+
ctx.body = {
|
|
1718
|
+
data: updatedAction
|
|
1719
|
+
};
|
|
1720
|
+
},
|
|
1721
|
+
async delete(ctx) {
|
|
1722
|
+
const actionId = ctx.params.actionId;
|
|
1723
|
+
const releaseId = ctx.params.releaseId;
|
|
1724
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1725
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1726
|
+
ctx.body = {
|
|
1727
|
+
data: deletedReleaseAction
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1973
1730
|
};
|
|
1974
|
-
|
|
1975
1731
|
const SETTINGS_SCHEMA = yup.object().shape({
|
|
1976
|
-
|
|
1732
|
+
defaultTimezone: yup.string().nullable().default(null)
|
|
1977
1733
|
}).required().noUnknown();
|
|
1978
1734
|
const validateSettings = validateYupSchema(SETTINGS_SCHEMA);
|
|
1979
|
-
|
|
1980
1735
|
const settingsController = {
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
// Data validation
|
|
1994
|
-
const settingsBody = ctx.request.body;
|
|
1995
|
-
const settings = await validateSettings(settingsBody);
|
|
1996
|
-
// Update
|
|
1997
|
-
const settingsService = getService('settings', {
|
|
1998
|
-
strapi
|
|
1999
|
-
});
|
|
2000
|
-
const updatedSettings = await settingsService.update({
|
|
2001
|
-
settings
|
|
2002
|
-
});
|
|
2003
|
-
// Response
|
|
2004
|
-
ctx.body = {
|
|
2005
|
-
data: updatedSettings
|
|
2006
|
-
};
|
|
2007
|
-
}
|
|
1736
|
+
async find(ctx) {
|
|
1737
|
+
const settingsService = getService("settings", { strapi });
|
|
1738
|
+
const settings2 = await settingsService.find();
|
|
1739
|
+
ctx.body = { data: settings2 };
|
|
1740
|
+
},
|
|
1741
|
+
async update(ctx) {
|
|
1742
|
+
const settingsBody = ctx.request.body;
|
|
1743
|
+
const settings2 = await validateSettings(settingsBody);
|
|
1744
|
+
const settingsService = getService("settings", { strapi });
|
|
1745
|
+
const updatedSettings = await settingsService.update({ settings: settings2 });
|
|
1746
|
+
ctx.body = { data: updatedSettings };
|
|
1747
|
+
}
|
|
2008
1748
|
};
|
|
2009
|
-
|
|
2010
1749
|
const controllers = {
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
1750
|
+
release: releaseController,
|
|
1751
|
+
"release-action": releaseActionController,
|
|
1752
|
+
settings: settingsController
|
|
2014
1753
|
};
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
1754
|
+
const release = {
|
|
1755
|
+
type: "admin",
|
|
1756
|
+
routes: [
|
|
1757
|
+
{
|
|
1758
|
+
method: "GET",
|
|
1759
|
+
path: "/mapEntriesToReleases",
|
|
1760
|
+
handler: "release.mapEntriesToReleases",
|
|
1761
|
+
config: {
|
|
1762
|
+
policies: [
|
|
1763
|
+
"admin::isAuthenticatedAdmin",
|
|
1764
|
+
{
|
|
1765
|
+
name: "admin::hasPermissions",
|
|
2023
1766
|
config: {
|
|
2024
|
-
|
|
2025
|
-
'admin::isAuthenticatedAdmin',
|
|
2026
|
-
{
|
|
2027
|
-
name: 'admin::hasPermissions',
|
|
2028
|
-
config: {
|
|
2029
|
-
actions: [
|
|
2030
|
-
'plugin::content-releases.read'
|
|
2031
|
-
]
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
]
|
|
1767
|
+
actions: ["plugin::content-releases.read"]
|
|
2035
1768
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
1769
|
+
}
|
|
1770
|
+
]
|
|
1771
|
+
}
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
method: "GET",
|
|
1775
|
+
path: "/getByDocumentAttached",
|
|
1776
|
+
handler: "release.findByDocumentAttached",
|
|
1777
|
+
config: {
|
|
1778
|
+
policies: [
|
|
1779
|
+
"admin::isAuthenticatedAdmin",
|
|
1780
|
+
{
|
|
1781
|
+
name: "admin::hasPermissions",
|
|
2041
1782
|
config: {
|
|
2042
|
-
|
|
2043
|
-
'admin::isAuthenticatedAdmin',
|
|
2044
|
-
{
|
|
2045
|
-
name: 'admin::hasPermissions',
|
|
2046
|
-
config: {
|
|
2047
|
-
actions: [
|
|
2048
|
-
'plugin::content-releases.read'
|
|
2049
|
-
]
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
]
|
|
1783
|
+
actions: ["plugin::content-releases.read"]
|
|
2053
1784
|
}
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
1785
|
+
}
|
|
1786
|
+
]
|
|
1787
|
+
}
|
|
1788
|
+
},
|
|
1789
|
+
{
|
|
1790
|
+
method: "POST",
|
|
1791
|
+
path: "/",
|
|
1792
|
+
handler: "release.create",
|
|
1793
|
+
config: {
|
|
1794
|
+
policies: [
|
|
1795
|
+
"admin::isAuthenticatedAdmin",
|
|
1796
|
+
{
|
|
1797
|
+
name: "admin::hasPermissions",
|
|
2059
1798
|
config: {
|
|
2060
|
-
|
|
2061
|
-
'admin::isAuthenticatedAdmin',
|
|
2062
|
-
{
|
|
2063
|
-
name: 'admin::hasPermissions',
|
|
2064
|
-
config: {
|
|
2065
|
-
actions: [
|
|
2066
|
-
'plugin::content-releases.create'
|
|
2067
|
-
]
|
|
2068
|
-
}
|
|
2069
|
-
}
|
|
2070
|
-
]
|
|
1799
|
+
actions: ["plugin::content-releases.create"]
|
|
2071
1800
|
}
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
1801
|
+
}
|
|
1802
|
+
]
|
|
1803
|
+
}
|
|
1804
|
+
},
|
|
1805
|
+
{
|
|
1806
|
+
method: "GET",
|
|
1807
|
+
path: "/",
|
|
1808
|
+
handler: "release.findPage",
|
|
1809
|
+
config: {
|
|
1810
|
+
policies: [
|
|
1811
|
+
"admin::isAuthenticatedAdmin",
|
|
1812
|
+
{
|
|
1813
|
+
name: "admin::hasPermissions",
|
|
2077
1814
|
config: {
|
|
2078
|
-
|
|
2079
|
-
'admin::isAuthenticatedAdmin',
|
|
2080
|
-
{
|
|
2081
|
-
name: 'admin::hasPermissions',
|
|
2082
|
-
config: {
|
|
2083
|
-
actions: [
|
|
2084
|
-
'plugin::content-releases.read'
|
|
2085
|
-
]
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
|
-
]
|
|
1815
|
+
actions: ["plugin::content-releases.read"]
|
|
2089
1816
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
1817
|
+
}
|
|
1818
|
+
]
|
|
1819
|
+
}
|
|
1820
|
+
},
|
|
1821
|
+
{
|
|
1822
|
+
method: "GET",
|
|
1823
|
+
path: "/:id",
|
|
1824
|
+
handler: "release.findOne",
|
|
1825
|
+
config: {
|
|
1826
|
+
policies: [
|
|
1827
|
+
"admin::isAuthenticatedAdmin",
|
|
1828
|
+
{
|
|
1829
|
+
name: "admin::hasPermissions",
|
|
2095
1830
|
config: {
|
|
2096
|
-
|
|
2097
|
-
'admin::isAuthenticatedAdmin',
|
|
2098
|
-
{
|
|
2099
|
-
name: 'admin::hasPermissions',
|
|
2100
|
-
config: {
|
|
2101
|
-
actions: [
|
|
2102
|
-
'plugin::content-releases.read'
|
|
2103
|
-
]
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
]
|
|
1831
|
+
actions: ["plugin::content-releases.read"]
|
|
2107
1832
|
}
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
1833
|
+
}
|
|
1834
|
+
]
|
|
1835
|
+
}
|
|
1836
|
+
},
|
|
1837
|
+
{
|
|
1838
|
+
method: "PUT",
|
|
1839
|
+
path: "/:id",
|
|
1840
|
+
handler: "release.update",
|
|
1841
|
+
config: {
|
|
1842
|
+
policies: [
|
|
1843
|
+
"admin::isAuthenticatedAdmin",
|
|
1844
|
+
{
|
|
1845
|
+
name: "admin::hasPermissions",
|
|
2113
1846
|
config: {
|
|
2114
|
-
|
|
2115
|
-
'admin::isAuthenticatedAdmin',
|
|
2116
|
-
{
|
|
2117
|
-
name: 'admin::hasPermissions',
|
|
2118
|
-
config: {
|
|
2119
|
-
actions: [
|
|
2120
|
-
'plugin::content-releases.update'
|
|
2121
|
-
]
|
|
2122
|
-
}
|
|
2123
|
-
}
|
|
2124
|
-
]
|
|
1847
|
+
actions: ["plugin::content-releases.update"]
|
|
2125
1848
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
1849
|
+
}
|
|
1850
|
+
]
|
|
1851
|
+
}
|
|
1852
|
+
},
|
|
1853
|
+
{
|
|
1854
|
+
method: "DELETE",
|
|
1855
|
+
path: "/:id",
|
|
1856
|
+
handler: "release.delete",
|
|
1857
|
+
config: {
|
|
1858
|
+
policies: [
|
|
1859
|
+
"admin::isAuthenticatedAdmin",
|
|
1860
|
+
{
|
|
1861
|
+
name: "admin::hasPermissions",
|
|
2131
1862
|
config: {
|
|
2132
|
-
|
|
2133
|
-
'admin::isAuthenticatedAdmin',
|
|
2134
|
-
{
|
|
2135
|
-
name: 'admin::hasPermissions',
|
|
2136
|
-
config: {
|
|
2137
|
-
actions: [
|
|
2138
|
-
'plugin::content-releases.delete'
|
|
2139
|
-
]
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
]
|
|
1863
|
+
actions: ["plugin::content-releases.delete"]
|
|
2143
1864
|
}
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
1865
|
+
}
|
|
1866
|
+
]
|
|
1867
|
+
}
|
|
1868
|
+
},
|
|
1869
|
+
{
|
|
1870
|
+
method: "POST",
|
|
1871
|
+
path: "/:id/publish",
|
|
1872
|
+
handler: "release.publish",
|
|
1873
|
+
config: {
|
|
1874
|
+
policies: [
|
|
1875
|
+
"admin::isAuthenticatedAdmin",
|
|
1876
|
+
{
|
|
1877
|
+
name: "admin::hasPermissions",
|
|
2149
1878
|
config: {
|
|
2150
|
-
|
|
2151
|
-
'admin::isAuthenticatedAdmin',
|
|
2152
|
-
{
|
|
2153
|
-
name: 'admin::hasPermissions',
|
|
2154
|
-
config: {
|
|
2155
|
-
actions: [
|
|
2156
|
-
'plugin::content-releases.publish'
|
|
2157
|
-
]
|
|
2158
|
-
}
|
|
2159
|
-
}
|
|
2160
|
-
]
|
|
1879
|
+
actions: ["plugin::content-releases.publish"]
|
|
2161
1880
|
}
|
|
2162
|
-
|
|
2163
|
-
|
|
1881
|
+
}
|
|
1882
|
+
]
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
]
|
|
2164
1886
|
};
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
1887
|
+
const releaseAction = {
|
|
1888
|
+
type: "admin",
|
|
1889
|
+
routes: [
|
|
1890
|
+
{
|
|
1891
|
+
method: "POST",
|
|
1892
|
+
path: "/:releaseId/actions",
|
|
1893
|
+
handler: "release-action.create",
|
|
1894
|
+
config: {
|
|
1895
|
+
policies: [
|
|
1896
|
+
"admin::isAuthenticatedAdmin",
|
|
1897
|
+
{
|
|
1898
|
+
name: "admin::hasPermissions",
|
|
2173
1899
|
config: {
|
|
2174
|
-
|
|
2175
|
-
'admin::isAuthenticatedAdmin',
|
|
2176
|
-
{
|
|
2177
|
-
name: 'admin::hasPermissions',
|
|
2178
|
-
config: {
|
|
2179
|
-
actions: [
|
|
2180
|
-
'plugin::content-releases.create-action'
|
|
2181
|
-
]
|
|
2182
|
-
}
|
|
2183
|
-
}
|
|
2184
|
-
]
|
|
1900
|
+
actions: ["plugin::content-releases.create-action"]
|
|
2185
1901
|
}
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
1902
|
+
}
|
|
1903
|
+
]
|
|
1904
|
+
}
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
method: "POST",
|
|
1908
|
+
path: "/:releaseId/actions/bulk",
|
|
1909
|
+
handler: "release-action.createMany",
|
|
1910
|
+
config: {
|
|
1911
|
+
policies: [
|
|
1912
|
+
"admin::isAuthenticatedAdmin",
|
|
1913
|
+
{
|
|
1914
|
+
name: "admin::hasPermissions",
|
|
2191
1915
|
config: {
|
|
2192
|
-
|
|
2193
|
-
'admin::isAuthenticatedAdmin',
|
|
2194
|
-
{
|
|
2195
|
-
name: 'admin::hasPermissions',
|
|
2196
|
-
config: {
|
|
2197
|
-
actions: [
|
|
2198
|
-
'plugin::content-releases.create-action'
|
|
2199
|
-
]
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
]
|
|
1916
|
+
actions: ["plugin::content-releases.create-action"]
|
|
2203
1917
|
}
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
1918
|
+
}
|
|
1919
|
+
]
|
|
1920
|
+
}
|
|
1921
|
+
},
|
|
1922
|
+
{
|
|
1923
|
+
method: "GET",
|
|
1924
|
+
path: "/:releaseId/actions",
|
|
1925
|
+
handler: "release-action.findMany",
|
|
1926
|
+
config: {
|
|
1927
|
+
policies: [
|
|
1928
|
+
"admin::isAuthenticatedAdmin",
|
|
1929
|
+
{
|
|
1930
|
+
name: "admin::hasPermissions",
|
|
2209
1931
|
config: {
|
|
2210
|
-
|
|
2211
|
-
'admin::isAuthenticatedAdmin',
|
|
2212
|
-
{
|
|
2213
|
-
name: 'admin::hasPermissions',
|
|
2214
|
-
config: {
|
|
2215
|
-
actions: [
|
|
2216
|
-
'plugin::content-releases.read'
|
|
2217
|
-
]
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
]
|
|
1932
|
+
actions: ["plugin::content-releases.read"]
|
|
2221
1933
|
}
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
1934
|
+
}
|
|
1935
|
+
]
|
|
1936
|
+
}
|
|
1937
|
+
},
|
|
1938
|
+
{
|
|
1939
|
+
method: "PUT",
|
|
1940
|
+
path: "/:releaseId/actions/:actionId",
|
|
1941
|
+
handler: "release-action.update",
|
|
1942
|
+
config: {
|
|
1943
|
+
policies: [
|
|
1944
|
+
"admin::isAuthenticatedAdmin",
|
|
1945
|
+
{
|
|
1946
|
+
name: "admin::hasPermissions",
|
|
2227
1947
|
config: {
|
|
2228
|
-
|
|
2229
|
-
'admin::isAuthenticatedAdmin',
|
|
2230
|
-
{
|
|
2231
|
-
name: 'admin::hasPermissions',
|
|
2232
|
-
config: {
|
|
2233
|
-
actions: [
|
|
2234
|
-
'plugin::content-releases.update'
|
|
2235
|
-
]
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
]
|
|
1948
|
+
actions: ["plugin::content-releases.update"]
|
|
2239
1949
|
}
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
1950
|
+
}
|
|
1951
|
+
]
|
|
1952
|
+
}
|
|
1953
|
+
},
|
|
1954
|
+
{
|
|
1955
|
+
method: "DELETE",
|
|
1956
|
+
path: "/:releaseId/actions/:actionId",
|
|
1957
|
+
handler: "release-action.delete",
|
|
1958
|
+
config: {
|
|
1959
|
+
policies: [
|
|
1960
|
+
"admin::isAuthenticatedAdmin",
|
|
1961
|
+
{
|
|
1962
|
+
name: "admin::hasPermissions",
|
|
2245
1963
|
config: {
|
|
2246
|
-
|
|
2247
|
-
'admin::isAuthenticatedAdmin',
|
|
2248
|
-
{
|
|
2249
|
-
name: 'admin::hasPermissions',
|
|
2250
|
-
config: {
|
|
2251
|
-
actions: [
|
|
2252
|
-
'plugin::content-releases.delete-action'
|
|
2253
|
-
]
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
]
|
|
1964
|
+
actions: ["plugin::content-releases.delete-action"]
|
|
2257
1965
|
}
|
|
2258
|
-
|
|
2259
|
-
|
|
1966
|
+
}
|
|
1967
|
+
]
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
]
|
|
2260
1971
|
};
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
1972
|
+
const settings = {
|
|
1973
|
+
type: "admin",
|
|
1974
|
+
routes: [
|
|
1975
|
+
{
|
|
1976
|
+
method: "GET",
|
|
1977
|
+
path: "/settings",
|
|
1978
|
+
handler: "settings.find",
|
|
1979
|
+
config: {
|
|
1980
|
+
policies: [
|
|
1981
|
+
"admin::isAuthenticatedAdmin",
|
|
1982
|
+
{
|
|
1983
|
+
name: "admin::hasPermissions",
|
|
2269
1984
|
config: {
|
|
2270
|
-
|
|
2271
|
-
'admin::isAuthenticatedAdmin',
|
|
2272
|
-
{
|
|
2273
|
-
name: 'admin::hasPermissions',
|
|
2274
|
-
config: {
|
|
2275
|
-
actions: [
|
|
2276
|
-
'plugin::content-releases.settings.read'
|
|
2277
|
-
]
|
|
2278
|
-
}
|
|
2279
|
-
}
|
|
2280
|
-
]
|
|
1985
|
+
actions: ["plugin::content-releases.settings.read"]
|
|
2281
1986
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
1987
|
+
}
|
|
1988
|
+
]
|
|
1989
|
+
}
|
|
1990
|
+
},
|
|
1991
|
+
{
|
|
1992
|
+
method: "PUT",
|
|
1993
|
+
path: "/settings",
|
|
1994
|
+
handler: "settings.update",
|
|
1995
|
+
config: {
|
|
1996
|
+
policies: [
|
|
1997
|
+
"admin::isAuthenticatedAdmin",
|
|
1998
|
+
{
|
|
1999
|
+
name: "admin::hasPermissions",
|
|
2287
2000
|
config: {
|
|
2288
|
-
|
|
2289
|
-
'admin::isAuthenticatedAdmin',
|
|
2290
|
-
{
|
|
2291
|
-
name: 'admin::hasPermissions',
|
|
2292
|
-
config: {
|
|
2293
|
-
actions: [
|
|
2294
|
-
'plugin::content-releases.settings.update'
|
|
2295
|
-
]
|
|
2296
|
-
}
|
|
2297
|
-
}
|
|
2298
|
-
]
|
|
2001
|
+
actions: ["plugin::content-releases.settings.update"]
|
|
2299
2002
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2003
|
+
}
|
|
2004
|
+
]
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
]
|
|
2302
2008
|
};
|
|
2303
|
-
|
|
2304
2009
|
const routes = {
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2010
|
+
settings,
|
|
2011
|
+
release,
|
|
2012
|
+
"release-action": releaseAction
|
|
2308
2013
|
};
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
if (strapi.ee.features.isEnabled('cms-content-releases')) {
|
|
2312
|
-
return {
|
|
2313
|
-
register,
|
|
2314
|
-
bootstrap,
|
|
2315
|
-
destroy,
|
|
2316
|
-
contentTypes,
|
|
2317
|
-
services,
|
|
2318
|
-
controllers,
|
|
2319
|
-
routes
|
|
2320
|
-
};
|
|
2321
|
-
}
|
|
2014
|
+
const getPlugin = () => {
|
|
2015
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
2322
2016
|
return {
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2017
|
+
register,
|
|
2018
|
+
bootstrap,
|
|
2019
|
+
destroy,
|
|
2020
|
+
contentTypes,
|
|
2021
|
+
services,
|
|
2022
|
+
controllers,
|
|
2023
|
+
routes
|
|
2327
2024
|
};
|
|
2025
|
+
}
|
|
2026
|
+
return {
|
|
2027
|
+
// Always return register, it handles its own feature check
|
|
2028
|
+
register,
|
|
2029
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
2030
|
+
contentTypes
|
|
2031
|
+
};
|
|
2032
|
+
};
|
|
2033
|
+
const index = getPlugin();
|
|
2034
|
+
export {
|
|
2035
|
+
index as default
|
|
2328
2036
|
};
|
|
2329
|
-
var index = getPlugin();
|
|
2330
|
-
|
|
2331
|
-
export { index as default };
|
|
2332
2037
|
//# sourceMappingURL=index.mjs.map
|