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