@strapi/content-type-builder 5.53.0 → 5.55.0
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/admin/components/AIChat/hooks/useAIFetch.js +6 -2
- package/dist/admin/components/AIChat/hooks/useAIFetch.js.map +1 -1
- package/dist/admin/components/AIChat/hooks/useAIFetch.mjs +7 -3
- package/dist/admin/components/AIChat/hooks/useAIFetch.mjs.map +1 -1
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.js +20 -0
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.js.map +1 -0
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.mjs +18 -0
- package/dist/admin/components/AIChat/hooks/usePrefetchAIToken.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.js +83 -59
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.js.map +1 -1
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.mjs +86 -62
- package/dist/admin/components/ContentTypeBuilderNav/ContentTypeBuilderNav.mjs.map +1 -1
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.js +112 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.mjs +110 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.js +159 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.mjs +157 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.js +311 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.mjs +309 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.js +379 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.mjs +376 -0
- package/dist/admin/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.js +172 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.mjs +166 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useContentTypeBuilderMenu.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.js +62 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.mjs +60 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useFolderActions.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.js +145 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.mjs +143 -0
- package/dist/admin/components/ContentTypeBuilderNav/hooks/useSortableTree.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.js +176 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.mjs +169 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/buildFolderTree.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.js +277 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.mjs +273 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/dropProjection.mjs.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.js +62 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.js.map +1 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.mjs +58 -0
- package/dist/admin/components/ContentTypeBuilderNav/lib/flatModel.mjs.map +1 -0
- package/dist/admin/components/DataManager/DataManagerContext.js.map +1 -1
- package/dist/admin/components/DataManager/DataManagerContext.mjs.map +1 -1
- package/dist/admin/components/DataManager/DataManagerProvider.js +70 -8
- package/dist/admin/components/DataManager/DataManagerProvider.js.map +1 -1
- package/dist/admin/components/DataManager/DataManagerProvider.mjs +70 -8
- package/dist/admin/components/DataManager/DataManagerProvider.mjs.map +1 -1
- package/dist/admin/components/DataManager/reducer.js +411 -33
- package/dist/admin/components/DataManager/reducer.js.map +1 -1
- package/dist/admin/components/DataManager/reducer.mjs +411 -33
- package/dist/admin/components/DataManager/reducer.mjs.map +1 -1
- package/dist/admin/components/DataManager/utils/cleanData.js +11 -2
- package/dist/admin/components/DataManager/utils/cleanData.js.map +1 -1
- package/dist/admin/components/DataManager/utils/cleanData.mjs +11 -2
- package/dist/admin/components/DataManager/utils/cleanData.mjs.map +1 -1
- package/dist/admin/components/DataManager/utils/contentStructure.js +150 -0
- package/dist/admin/components/DataManager/utils/contentStructure.js.map +1 -0
- package/dist/admin/components/DataManager/utils/contentStructure.mjs +133 -0
- package/dist/admin/components/DataManager/utils/contentStructure.mjs.map +1 -0
- package/dist/admin/components/FolderSelect.js +200 -0
- package/dist/admin/components/FolderSelect.js.map +1 -0
- package/dist/admin/components/FolderSelect.mjs +198 -0
- package/dist/admin/components/FolderSelect.mjs.map +1 -0
- package/dist/admin/components/FormModal/FormModal.js +23 -2
- package/dist/admin/components/FormModal/FormModal.js.map +1 -1
- package/dist/admin/components/FormModal/FormModal.mjs +23 -2
- package/dist/admin/components/FormModal/FormModal.mjs.map +1 -1
- package/dist/admin/components/FormModal/contentType/contentTypeForm.js +15 -18
- package/dist/admin/components/FormModal/contentType/contentTypeForm.js.map +1 -1
- package/dist/admin/components/FormModal/contentType/contentTypeForm.mjs +15 -18
- package/dist/admin/components/FormModal/contentType/contentTypeForm.mjs.map +1 -1
- package/dist/admin/components/FormModal/reducer.js +6 -0
- package/dist/admin/components/FormModal/reducer.js.map +1 -1
- package/dist/admin/components/FormModal/reducer.mjs +6 -0
- package/dist/admin/components/FormModal/reducer.mjs.map +1 -1
- package/dist/admin/pages/App/index.js +2 -5
- package/dist/admin/pages/App/index.js.map +1 -1
- package/dist/admin/pages/App/index.mjs +2 -5
- package/dist/admin/pages/App/index.mjs.map +1 -1
- package/dist/admin/src/components/AIChat/hooks/usePrefetchAIToken.d.ts +1 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/DeleteFolderDialog.d.ts +16 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/FolderNameField.d.ts +12 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/FolderNavSection.d.ts +14 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/components/FolderNav/SortableTreeRow.d.ts +38 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/{useContentTypeBuilderMenu.d.ts → hooks/useContentTypeBuilderMenu.d.ts} +17 -7
- package/dist/admin/src/components/ContentTypeBuilderNav/hooks/useFolderActions.d.ts +42 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/hooks/useSortableTree.d.ts +53 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/lib/buildFolderTree.d.ts +42 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/lib/dropProjection.d.ts +58 -0
- package/dist/admin/src/components/ContentTypeBuilderNav/lib/flatModel.d.ts +18 -0
- package/dist/admin/src/components/DataManager/DataManagerContext.d.ts +41 -0
- package/dist/admin/src/components/DataManager/reducer.d.ts +105 -1
- package/dist/admin/src/components/DataManager/utils/cleanData.d.ts +5 -1
- package/dist/admin/src/components/DataManager/utils/contentStructure.d.ts +68 -0
- package/dist/admin/src/components/FolderSelect.d.ts +18 -0
- package/dist/admin/src/components/FormModal/contentType/contentTypeForm.d.ts +0 -21
- package/dist/admin/src/components/FormModal/forms/forms.d.ts +0 -10
- package/dist/admin/src/components/FormModal/reducer.d.ts +7 -0
- package/dist/admin/src/utils/folderTelemetry.d.ts +5 -0
- package/dist/admin/translations/en.json.js +23 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +23 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/admin/utils/folderTelemetry.js +19 -0
- package/dist/admin/utils/folderTelemetry.js.map +1 -0
- package/dist/admin/utils/folderTelemetry.mjs +16 -0
- package/dist/admin/utils/folderTelemetry.mjs.map +1 -0
- package/dist/server/controllers/schema.js +1 -1
- package/dist/server/controllers/schema.js.map +1 -1
- package/dist/server/controllers/schema.mjs +1 -1
- package/dist/server/controllers/schema.mjs.map +1 -1
- package/dist/server/controllers/validation/common.js +3 -0
- package/dist/server/controllers/validation/common.js.map +1 -1
- package/dist/server/controllers/validation/common.mjs +3 -1
- package/dist/server/controllers/validation/common.mjs.map +1 -1
- package/dist/server/controllers/validation/content-structure.js +177 -0
- package/dist/server/controllers/validation/content-structure.js.map +1 -0
- package/dist/server/controllers/validation/content-structure.mjs +173 -0
- package/dist/server/controllers/validation/content-structure.mjs.map +1 -0
- package/dist/server/controllers/validation/schema.js +3 -1
- package/dist/server/controllers/validation/schema.js.map +1 -1
- package/dist/server/controllers/validation/schema.mjs +3 -1
- package/dist/server/controllers/validation/schema.mjs.map +1 -1
- package/dist/server/register.js +1 -1
- package/dist/server/register.js.map +1 -1
- package/dist/server/register.mjs +1 -1
- package/dist/server/register.mjs.map +1 -1
- package/dist/server/routes/content-api.js +4 -4
- package/dist/server/routes/content-api.js.map +1 -1
- package/dist/server/routes/content-api.mjs +4 -4
- package/dist/server/routes/content-api.mjs.map +1 -1
- package/dist/server/services/api-handler.js +37 -4
- package/dist/server/services/api-handler.js.map +1 -1
- package/dist/server/services/api-handler.mjs +36 -5
- package/dist/server/services/api-handler.mjs.map +1 -1
- package/dist/server/services/content-structure.js +174 -0
- package/dist/server/services/content-structure.js.map +1 -0
- package/dist/server/services/content-structure.mjs +172 -0
- package/dist/server/services/content-structure.mjs.map +1 -0
- package/dist/server/services/content-types.js +196 -44
- package/dist/server/services/content-types.js.map +1 -1
- package/dist/server/services/content-types.mjs +195 -45
- package/dist/server/services/content-types.mjs.map +1 -1
- package/dist/server/services/index.js +3 -1
- package/dist/server/services/index.js.map +1 -1
- package/dist/server/services/index.mjs +3 -1
- package/dist/server/services/index.mjs.map +1 -1
- package/dist/server/services/schema-builder/index.js +4 -2
- package/dist/server/services/schema-builder/index.js.map +1 -1
- package/dist/server/services/schema-builder/index.mjs +4 -2
- package/dist/server/services/schema-builder/index.mjs.map +1 -1
- package/dist/server/services/schema-builder/schema-handler.js +19 -17
- package/dist/server/services/schema-builder/schema-handler.js.map +1 -1
- package/dist/server/services/schema-builder/schema-handler.mjs +19 -17
- package/dist/server/services/schema-builder/schema-handler.mjs.map +1 -1
- package/dist/server/services/schema-mutation.js +46 -0
- package/dist/server/services/schema-mutation.js.map +1 -0
- package/dist/server/services/schema-mutation.mjs +43 -0
- package/dist/server/services/schema-mutation.mjs.map +1 -0
- package/dist/server/services/schema.js +134 -79
- package/dist/server/services/schema.js.map +1 -1
- package/dist/server/services/schema.mjs +135 -80
- package/dist/server/services/schema.mjs.map +1 -1
- package/dist/server/src/controllers/schema.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/common.d.ts +1 -0
- package/dist/server/src/controllers/validation/common.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/content-structure.d.ts +56 -0
- package/dist/server/src/controllers/validation/content-structure.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/schema.d.ts +35 -0
- package/dist/server/src/controllers/validation/schema.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +3 -0
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/routes/content-api.d.ts.map +1 -1
- package/dist/server/src/services/api-handler.d.ts +13 -1
- package/dist/server/src/services/api-handler.d.ts.map +1 -1
- package/dist/server/src/services/content-structure.d.ts +37 -0
- package/dist/server/src/services/content-structure.d.ts.map +1 -0
- package/dist/server/src/services/content-types.d.ts +4 -0
- package/dist/server/src/services/content-types.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +4 -0
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/schema-builder/index.d.ts +3 -1
- package/dist/server/src/services/schema-builder/index.d.ts.map +1 -1
- package/dist/server/src/services/schema-builder/schema-handler.d.ts.map +1 -1
- package/dist/server/src/services/schema-mutation.d.ts +26 -0
- package/dist/server/src/services/schema-mutation.d.ts.map +1 -0
- package/dist/server/src/services/schema.d.ts +1 -0
- package/dist/server/src/services/schema.d.ts.map +1 -1
- package/dist/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/server/utils/index.js.map +1 -1
- package/dist/server/utils/index.mjs.map +1 -1
- package/package.json +8 -8
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.js +0 -157
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.js.map +0 -1
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.mjs +0 -151
- package/dist/admin/components/ContentTypeBuilderNav/useContentTypeBuilderMenu.mjs.map +0 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var utils = require('@strapi/utils');
|
|
4
4
|
var fp = require('lodash/fp');
|
|
5
5
|
var index = require('./schema-builder/index.js');
|
|
6
|
+
var schemaMutation = require('./schema-mutation.js');
|
|
6
7
|
var index$1 = require('../utils/index.js');
|
|
7
8
|
var contentTypes = require('./content-types.js');
|
|
8
9
|
|
|
@@ -104,7 +105,10 @@ const getSchema = async ()=>{
|
|
|
104
105
|
attributes: formatAttributes(component)
|
|
105
106
|
};
|
|
106
107
|
}, strapi.components);
|
|
108
|
+
const coreContentStructure = strapi.get('content-structure');
|
|
109
|
+
const contentStructure = await coreContentStructure.getCleanedFile();
|
|
107
110
|
return {
|
|
111
|
+
contentStructure,
|
|
108
112
|
contentTypes: contentTypes$1,
|
|
109
113
|
components
|
|
110
114
|
};
|
|
@@ -112,105 +116,156 @@ const getSchema = async ()=>{
|
|
|
112
116
|
const updateSchema = async (schema)=>{
|
|
113
117
|
const builder = index();
|
|
114
118
|
const apiHandler = index$1.getService('api-handler');
|
|
115
|
-
const { components, contentTypes } = schema;
|
|
119
|
+
const { components, contentTypes: contentTypes$1, contentStructure } = schema;
|
|
120
|
+
// Reject protected/plugin deletes before builders, API backups, or folder reconciliation can
|
|
121
|
+
// mutate anything. This is the server-side invariant for crafted update requests.
|
|
122
|
+
contentTypes$1.filter((contentType)=>contentType.action === 'delete').forEach((contentType)=>contentTypes.assertCTBOwnedApplicationContentType(contentType.uid));
|
|
116
123
|
// pre-process data
|
|
117
124
|
removeEmptyDefaultsOnUpdates(schema);
|
|
118
125
|
removeDeletedUIDTargetFieldsOnUpdates(schema);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
const upsertedUids = new Map();
|
|
127
|
+
const deletedUids = new Set();
|
|
128
|
+
const generatedApiNames = [];
|
|
129
|
+
const backedUpApiUids = [];
|
|
130
|
+
let schemaAlreadyRolledBack = false;
|
|
131
|
+
const APIsToDelete = contentTypes$1.filter((contentType)=>contentType.action === 'delete').map((contentType)=>contentType.uid);
|
|
132
|
+
try {
|
|
133
|
+
for (const contentType of contentTypes$1){
|
|
134
|
+
if (contentType.action === 'create') {
|
|
135
|
+
upsertedUids.set(contentType.uid, contentType.kind ?? 'collectionType');
|
|
136
|
+
} else if (contentType.action === 'update' && contentType.kind) {
|
|
137
|
+
// A kind switch in the same save must override the registry's stale kind,
|
|
138
|
+
// otherwise a folder assignment into the new section fails validation.
|
|
139
|
+
upsertedUids.set(contentType.uid, contentType.kind);
|
|
140
|
+
} else if (contentType.action === 'delete') {
|
|
141
|
+
deletedUids.add(contentType.uid);
|
|
142
|
+
}
|
|
126
143
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
144
|
+
// Validate folder references before anything is written. This will throw if invalid.
|
|
145
|
+
index$1.getService('content-structure').validateFromUpdate({
|
|
146
|
+
incomingStructure: contentStructure,
|
|
147
|
+
upsertedUids,
|
|
148
|
+
deletedUids
|
|
149
|
+
});
|
|
150
|
+
// we pre create empty typesk
|
|
151
|
+
for (const contentType of contentTypes$1){
|
|
152
|
+
if (contentType.action === 'create') {
|
|
153
|
+
builder.createContentType({
|
|
154
|
+
...contentType,
|
|
155
|
+
attributes: {}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
135
158
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return acc;
|
|
143
|
-
}, {}));
|
|
144
|
-
if (!contentType.plugin) {
|
|
145
|
-
await index$1.getService('content-types').generateAPI({
|
|
146
|
-
displayName: contentType.displayName,
|
|
147
|
-
singularName: contentType.singularName,
|
|
148
|
-
pluralName: contentType.pluralName,
|
|
149
|
-
kind: contentType.kind
|
|
159
|
+
// we pre create empty types
|
|
160
|
+
for (const component of components){
|
|
161
|
+
if (component.action === 'create') {
|
|
162
|
+
builder.createComponent({
|
|
163
|
+
...component,
|
|
164
|
+
attributes: {}
|
|
150
165
|
});
|
|
151
166
|
}
|
|
152
167
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// NOTE: handle renaming migrations here by comparing attr name & attr.properties.name
|
|
158
|
-
if (attr.action === 'delete') {
|
|
159
|
-
return acc;
|
|
160
|
-
}
|
|
168
|
+
for (const contentType of contentTypes$1){
|
|
169
|
+
const { action, uid } = contentType;
|
|
170
|
+
if (action === 'create') {
|
|
171
|
+
builder.createContentTypeAttributes(uid, contentType.attributes.reduce((acc, attr)=>{
|
|
161
172
|
acc[attr.name] = attr.properties;
|
|
162
173
|
return acc;
|
|
163
|
-
}, {})
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
174
|
+
}, {}));
|
|
175
|
+
if (!contentType.plugin) {
|
|
176
|
+
// Track before generation because a generator failure can leave a partial skeleton.
|
|
177
|
+
generatedApiNames.push(contentType.singularName);
|
|
178
|
+
await index$1.getService('content-types').generateAPI({
|
|
179
|
+
displayName: contentType.displayName,
|
|
180
|
+
singularName: contentType.singularName,
|
|
181
|
+
pluralName: contentType.pluralName,
|
|
182
|
+
kind: contentType.kind
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (action === 'update') {
|
|
187
|
+
builder.editContentType({
|
|
188
|
+
...contentType,
|
|
189
|
+
attributes: contentType.attributes.reduce((acc, attr)=>{
|
|
190
|
+
// NOTE: handle renaming migrations here by comparing attr name & attr.properties.name
|
|
191
|
+
if (attr.action === 'delete') {
|
|
192
|
+
return acc;
|
|
193
|
+
}
|
|
194
|
+
acc[attr.name] = attr.properties;
|
|
184
195
|
return acc;
|
|
185
|
-
}
|
|
196
|
+
}, {})
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (action === 'delete') {
|
|
200
|
+
builder.deleteContentType(uid);
|
|
201
|
+
await apiHandler.backup(uid);
|
|
202
|
+
backedUpApiUids.push(uid);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const component of components){
|
|
206
|
+
const { action, uid } = component;
|
|
207
|
+
if (action === 'create') {
|
|
208
|
+
builder.createComponentAttributes(uid, component.attributes.reduce((acc, attr)=>{
|
|
186
209
|
acc[attr.name] = attr.properties;
|
|
187
210
|
return acc;
|
|
188
|
-
}, {})
|
|
189
|
-
}
|
|
211
|
+
}, {}));
|
|
212
|
+
}
|
|
213
|
+
if (action === 'update') {
|
|
214
|
+
builder.editComponent({
|
|
215
|
+
...component,
|
|
216
|
+
attributes: component.attributes.reduce((acc, attr)=>{
|
|
217
|
+
if (attr.action === 'delete') {
|
|
218
|
+
return acc;
|
|
219
|
+
}
|
|
220
|
+
acc[attr.name] = attr.properties;
|
|
221
|
+
return acc;
|
|
222
|
+
}, {})
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (action === 'delete') {
|
|
226
|
+
builder.deleteComponent(uid);
|
|
227
|
+
}
|
|
190
228
|
}
|
|
191
|
-
|
|
192
|
-
|
|
229
|
+
// run sanity checks on the schema
|
|
230
|
+
// Relations target existing types
|
|
231
|
+
// Bidirectional relation have their counterpart in the schema
|
|
232
|
+
// Components target existing components
|
|
233
|
+
// Nested components target existing components
|
|
234
|
+
// Dynamic zones target existing components
|
|
235
|
+
const schemaFilesWritten = await builder.writeFiles();
|
|
236
|
+
if (!schemaFilesWritten) {
|
|
237
|
+
schemaAlreadyRolledBack = true;
|
|
238
|
+
throw new utils.errors.ApplicationError('Invalid schema edition');
|
|
193
239
|
}
|
|
194
|
-
}
|
|
195
|
-
// run sanity checks on the schema
|
|
196
|
-
// Relations target existing types
|
|
197
|
-
// Bidirectional relation have their counterpart in the schema
|
|
198
|
-
// Components target existing components
|
|
199
|
-
// Nested components target existing components
|
|
200
|
-
// Dynamic zones target existing components
|
|
201
|
-
const APIsToDelete = contentTypes.filter((ct)=>ct.action === 'delete').map((ct)=>ct.uid);
|
|
202
|
-
await builder.writeFiles();
|
|
203
|
-
try {
|
|
204
240
|
for (const uid of APIsToDelete){
|
|
205
|
-
await apiHandler.clear(uid
|
|
241
|
+
await apiHandler.clear(uid, {
|
|
242
|
+
preserveBackup: true
|
|
243
|
+
});
|
|
206
244
|
}
|
|
245
|
+
// Commit the single folder file last. Every preceding filesystem mutation can be restored.
|
|
246
|
+
await index$1.getService('content-structure').commitFromUpdate({
|
|
247
|
+
incomingStructure: contentStructure,
|
|
248
|
+
deletedUids
|
|
249
|
+
});
|
|
207
250
|
} catch (error) {
|
|
251
|
+
await schemaMutation.rollbackSchemaMutation({
|
|
252
|
+
builder,
|
|
253
|
+
apiHandler,
|
|
254
|
+
backedUpApiUids,
|
|
255
|
+
generatedApiNames,
|
|
256
|
+
schemaAlreadyRolledBack
|
|
257
|
+
});
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
// Backup cleanup is deliberately outside the compensating boundary: at this point all
|
|
261
|
+
// user-visible artifacts, including groups.json, have committed.
|
|
262
|
+
for (const error of (await schemaMutation.finalizeSchemaMutation({
|
|
263
|
+
apiHandler,
|
|
264
|
+
backedUpApiUids
|
|
265
|
+
}))){
|
|
208
266
|
strapi.log.error(error);
|
|
209
|
-
for (const uid of APIsToDelete){
|
|
210
|
-
await apiHandler.rollback(uid);
|
|
211
|
-
}
|
|
212
267
|
}
|
|
213
|
-
for (const contentType of contentTypes){
|
|
268
|
+
for (const contentType of contentTypes$1){
|
|
214
269
|
if (contentType.action === 'delete') {
|
|
215
270
|
strapi.eventHub.emit('content-type.delete', {
|
|
216
271
|
contentType: builder.contentTypes.get(contentType.uid)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sources":["../../../server/src/services/schema.ts"],"sourcesContent":["import { contentTypes as contentTypesUtils } from '@strapi/utils';\nimport { mapValues } from 'lodash/fp';\n\nimport type { Schema } from '@strapi/types';\n\nimport createBuilder from './schema-builder';\nimport { getService } from '../utils';\nimport type { Schema as CTBSchema } from '../controllers/validation/schema';\nimport { getRestrictRelationsTo, isContentTypeVisible } from './content-types';\n\nconst removeEmptyDefaultsOnUpdates = (schema: CTBSchema) => {\n schema.components.forEach((component) => {\n if (component.action === 'delete') {\n return;\n }\n\n component.attributes.forEach((attribute) => {\n if (attribute.action === 'update') {\n const { properties } = attribute;\n\n if ('default' in properties && properties.default === '') {\n properties.default = undefined;\n }\n }\n });\n });\n\n schema.contentTypes.forEach((contentType) => {\n if (contentType.action === 'delete') {\n return;\n }\n\n contentType.attributes.forEach((attribute) => {\n if (attribute.action === 'update') {\n const { properties } = attribute;\n\n if ('default' in properties && properties.default === '') {\n properties.default = undefined;\n }\n }\n });\n });\n};\n\nconst removeDeletedUIDTargetFieldsOnUpdates = (schema: CTBSchema) => {\n schema.contentTypes.forEach((contentType) => {\n if (contentType.action === 'delete') {\n return;\n }\n\n contentType.attributes.forEach((attribute) => {\n if (attribute.action === 'update') {\n const { properties } = attribute;\n\n if (\n properties.type === 'uid' &&\n properties.targetField &&\n !contentType.attributes.find((attr) => attr.name === properties.targetField)\n ) {\n properties.targetField = undefined;\n }\n }\n });\n });\n};\n\nconst formatAttributes = (model: any) => {\n const { getVisibleAttributes } = contentTypesUtils;\n\n // only get attributes that can be seen in the CTB\n return getVisibleAttributes(model).map((key) => {\n return { ...formatAttribute(model.attributes[key]), name: key };\n });\n};\n\nexport const formatAttribute = (attribute: Schema.Attribute.AnyAttribute & Record<string, any>) => {\n if (attribute.type === 'relation') {\n return {\n ...attribute,\n targetAttribute: attribute.inversedBy || attribute.mappedBy || null,\n // Explicitly preserve conditions if they exist\n ...(attribute.conditions && { conditions: attribute.conditions }),\n };\n }\n\n return attribute;\n};\n\nexport const getSchema = async () => {\n const contentTypes = mapValues((contentType) => {\n const {\n uid,\n options,\n globalId,\n pluginOptions,\n kind,\n modelName,\n plugin,\n collectionName,\n info,\n modelType,\n } = contentType;\n\n return {\n uid,\n modelName,\n kind,\n globalId,\n options,\n pluginOptions,\n plugin,\n collectionName,\n info,\n modelType,\n attributes: formatAttributes(contentType),\n visible: isContentTypeVisible(contentType),\n restrictRelationsTo: getRestrictRelationsTo(contentType),\n };\n }, strapi.contentTypes);\n\n const components = mapValues((component) => {\n const { uid, globalId, modelName, collectionName, info, category, modelType } = component;\n\n return {\n uid,\n modelName,\n globalId,\n modelType,\n collectionName,\n category,\n info,\n attributes: formatAttributes(component),\n };\n }, strapi.components);\n\n return {\n contentTypes,\n components,\n };\n};\n\nexport const updateSchema = async (schema: CTBSchema) => {\n const builder = createBuilder();\n const apiHandler = getService('api-handler');\n\n const { components, contentTypes } = schema;\n\n // pre-process data\n removeEmptyDefaultsOnUpdates(schema);\n removeDeletedUIDTargetFieldsOnUpdates(schema);\n\n // we pre create empty typesk\n for (const contentType of contentTypes) {\n if (contentType.action === 'create') {\n builder.createContentType({\n ...contentType,\n attributes: {},\n });\n }\n }\n\n // we pre create empty types\n for (const component of components) {\n if (component.action === 'create') {\n builder.createComponent({\n ...component,\n attributes: {},\n });\n }\n }\n\n for (const contentType of contentTypes) {\n const { action, uid } = contentType;\n\n if (action === 'create') {\n builder.createContentTypeAttributes(\n uid,\n contentType.attributes.reduce((acc: any, attr: any) => {\n acc[attr.name] = attr.properties;\n return acc;\n }, {})\n );\n\n if (!contentType.plugin) {\n await getService('content-types').generateAPI({\n displayName: contentType!.displayName,\n singularName: contentType!.singularName,\n pluralName: contentType!.pluralName,\n kind: contentType!.kind,\n });\n }\n }\n\n if (action === 'update') {\n builder.editContentType({\n ...contentType,\n attributes: contentType.attributes.reduce((acc: any, attr: any) => {\n // NOTE: handle renaming migrations here by comparing attr name & attr.properties.name\n\n if (attr.action === 'delete') {\n return acc;\n }\n\n acc[attr.name] = attr.properties;\n return acc;\n }, {}),\n });\n }\n\n if (action === 'delete') {\n builder.deleteContentType(uid);\n await apiHandler.backup(uid);\n }\n }\n\n for (const component of components) {\n const { action, uid } = component;\n\n if (action === 'create') {\n builder.createComponentAttributes(\n uid,\n component.attributes.reduce((acc: any, attr: any) => {\n acc[attr.name] = attr.properties;\n return acc;\n }, {})\n );\n }\n\n if (action === 'update') {\n builder.editComponent({\n ...component,\n attributes: component.attributes.reduce((acc: any, attr: any) => {\n if (attr.action === 'delete') {\n return acc;\n }\n\n acc[attr.name] = attr.properties;\n return acc;\n }, {}),\n });\n }\n\n if (action === 'delete') {\n builder.deleteComponent(uid);\n }\n }\n\n // run sanity checks on the schema\n // Relations target existing types\n // Bidirectional relation have their counterpart in the schema\n // Components target existing components\n // Nested components target existing components\n // Dynamic zones target existing components\n\n const APIsToDelete = contentTypes\n .filter((ct: any) => ct.action === 'delete')\n .map((ct: any) => ct.uid);\n\n await builder.writeFiles();\n\n try {\n for (const uid of APIsToDelete) {\n await apiHandler.clear(uid);\n }\n } catch (error) {\n strapi.log.error(error);\n for (const uid of APIsToDelete) {\n await apiHandler.rollback(uid);\n }\n }\n\n for (const contentType of contentTypes) {\n if (contentType.action === 'delete') {\n strapi.eventHub.emit('content-type.delete', {\n contentType: builder.contentTypes.get(contentType.uid),\n });\n }\n\n if (contentType.action === 'update') {\n strapi.eventHub.emit('content-type.update', {\n contentType: builder.contentTypes.get(contentType.uid),\n });\n }\n\n if (contentType.action === 'create') {\n strapi.eventHub.emit('content-type.create', {\n contentType: builder.contentTypes.get(contentType.uid),\n });\n }\n }\n\n for (const component of components) {\n if (component.action === 'delete') {\n strapi.eventHub.emit('component.delete', {\n component: builder.components.get(component.uid),\n });\n }\n\n if (component.action === 'update') {\n strapi.eventHub.emit('component.update', {\n component: builder.components.get(component.uid),\n });\n }\n\n if (component.action === 'create') {\n strapi.eventHub.emit('component.create', {\n component: builder.components.get(component.uid),\n });\n }\n }\n};\n"],"names":["removeEmptyDefaultsOnUpdates","schema","components","forEach","component","action","attributes","attribute","properties","default","undefined","contentTypes","contentType","removeDeletedUIDTargetFieldsOnUpdates","type","targetField","find","attr","name","formatAttributes","model","getVisibleAttributes","contentTypesUtils","map","key","formatAttribute","targetAttribute","inversedBy","mappedBy","conditions","getSchema","mapValues","uid","options","globalId","pluginOptions","kind","modelName","plugin","collectionName","info","modelType","visible","isContentTypeVisible","restrictRelationsTo","getRestrictRelationsTo","strapi","category","updateSchema","builder","createBuilder","apiHandler","getService","createContentType","createComponent","createContentTypeAttributes","reduce","acc","generateAPI","displayName","singularName","pluralName","editContentType","deleteContentType","backup","createComponentAttributes","editComponent","deleteComponent","APIsToDelete","filter","ct","writeFiles","clear","error","log","rollback","eventHub","emit","get"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schema.js","sources":["../../../server/src/services/schema.ts"],"sourcesContent":["import { contentTypes as contentTypesUtils, errors } from '@strapi/utils';\nimport { mapValues } from 'lodash/fp';\n\nimport type { Schema } from '@strapi/types';\n\nimport createBuilder from './schema-builder';\nimport { finalizeSchemaMutation, rollbackSchemaMutation } from './schema-mutation';\nimport { getService } from '../utils';\nimport type { Schema as CTBSchema } from '../controllers/validation/schema';\nimport {\n assertCTBOwnedApplicationContentType,\n getRestrictRelationsTo,\n isContentTypeVisible,\n} from './content-types';\nimport type { CoreContentStructureService } from './content-structure';\n\ntype ContentTypeKind = 'collectionType' | 'singleType';\n\nconst removeEmptyDefaultsOnUpdates = (schema: CTBSchema) => {\n schema.components.forEach((component) => {\n if (component.action === 'delete') {\n return;\n }\n\n component.attributes.forEach((attribute) => {\n if (attribute.action === 'update') {\n const { properties } = attribute;\n\n if ('default' in properties && properties.default === '') {\n properties.default = undefined;\n }\n }\n });\n });\n\n schema.contentTypes.forEach((contentType) => {\n if (contentType.action === 'delete') {\n return;\n }\n\n contentType.attributes.forEach((attribute) => {\n if (attribute.action === 'update') {\n const { properties } = attribute;\n\n if ('default' in properties && properties.default === '') {\n properties.default = undefined;\n }\n }\n });\n });\n};\n\nconst removeDeletedUIDTargetFieldsOnUpdates = (schema: CTBSchema) => {\n schema.contentTypes.forEach((contentType) => {\n if (contentType.action === 'delete') {\n return;\n }\n\n contentType.attributes.forEach((attribute) => {\n if (attribute.action === 'update') {\n const { properties } = attribute;\n\n if (\n properties.type === 'uid' &&\n properties.targetField &&\n !contentType.attributes.find((attr) => attr.name === properties.targetField)\n ) {\n properties.targetField = undefined;\n }\n }\n });\n });\n};\n\nconst formatAttributes = (model: any) => {\n const { getVisibleAttributes } = contentTypesUtils;\n\n // only get attributes that can be seen in the CTB\n return getVisibleAttributes(model).map((key) => {\n return { ...formatAttribute(model.attributes[key]), name: key };\n });\n};\n\nexport const formatAttribute = (attribute: Schema.Attribute.AnyAttribute & Record<string, any>) => {\n if (attribute.type === 'relation') {\n return {\n ...attribute,\n targetAttribute: attribute.inversedBy || attribute.mappedBy || null,\n // Explicitly preserve conditions if they exist\n ...(attribute.conditions && { conditions: attribute.conditions }),\n };\n }\n\n return attribute;\n};\n\nexport const getSchema = async () => {\n const contentTypes = mapValues((contentType) => {\n const {\n uid,\n options,\n globalId,\n pluginOptions,\n kind,\n modelName,\n plugin,\n collectionName,\n info,\n modelType,\n } = contentType;\n\n return {\n uid,\n modelName,\n kind,\n globalId,\n options,\n pluginOptions,\n plugin,\n collectionName,\n info,\n modelType,\n attributes: formatAttributes(contentType),\n visible: isContentTypeVisible(contentType),\n restrictRelationsTo: getRestrictRelationsTo(contentType),\n };\n }, strapi.contentTypes);\n\n const components = mapValues((component) => {\n const { uid, globalId, modelName, collectionName, info, category, modelType } = component;\n\n return {\n uid,\n modelName,\n globalId,\n modelType,\n collectionName,\n category,\n info,\n attributes: formatAttributes(component),\n };\n }, strapi.components);\n\n const coreContentStructure: CoreContentStructureService = strapi.get('content-structure');\n const contentStructure = await coreContentStructure.getCleanedFile();\n\n return {\n contentStructure,\n contentTypes,\n components,\n };\n};\n\nexport const updateSchema = async (schema: CTBSchema) => {\n const builder = createBuilder();\n const apiHandler = getService('api-handler') as typeof import('./api-handler');\n\n const { components, contentTypes, contentStructure } = schema;\n\n // Reject protected/plugin deletes before builders, API backups, or folder reconciliation can\n // mutate anything. This is the server-side invariant for crafted update requests.\n contentTypes\n .filter((contentType) => contentType.action === 'delete')\n .forEach((contentType) => assertCTBOwnedApplicationContentType(contentType.uid));\n\n // pre-process data\n removeEmptyDefaultsOnUpdates(schema);\n removeDeletedUIDTargetFieldsOnUpdates(schema);\n\n const upsertedUids = new Map<string, ContentTypeKind>();\n const deletedUids = new Set<string>();\n const generatedApiNames: string[] = [];\n const backedUpApiUids: string[] = [];\n let schemaAlreadyRolledBack = false;\n const APIsToDelete = contentTypes\n .filter((contentType) => contentType.action === 'delete')\n .map((contentType) => contentType.uid);\n\n try {\n for (const contentType of contentTypes) {\n if (contentType.action === 'create') {\n upsertedUids.set(contentType.uid, contentType.kind ?? 'collectionType');\n } else if (contentType.action === 'update' && contentType.kind) {\n // A kind switch in the same save must override the registry's stale kind,\n // otherwise a folder assignment into the new section fails validation.\n upsertedUids.set(contentType.uid, contentType.kind);\n } else if (contentType.action === 'delete') {\n deletedUids.add(contentType.uid);\n }\n }\n\n // Validate folder references before anything is written. This will throw if invalid.\n getService('content-structure').validateFromUpdate({\n incomingStructure: contentStructure,\n upsertedUids,\n deletedUids,\n });\n\n // we pre create empty typesk\n for (const contentType of contentTypes) {\n if (contentType.action === 'create') {\n builder.createContentType({\n ...contentType,\n attributes: {},\n });\n }\n }\n\n // we pre create empty types\n for (const component of components) {\n if (component.action === 'create') {\n builder.createComponent({\n ...component,\n attributes: {},\n });\n }\n }\n\n for (const contentType of contentTypes) {\n const { action, uid } = contentType;\n\n if (action === 'create') {\n builder.createContentTypeAttributes(\n uid,\n contentType.attributes.reduce((acc: any, attr: any) => {\n acc[attr.name] = attr.properties;\n return acc;\n }, {})\n );\n\n if (!contentType.plugin) {\n // Track before generation because a generator failure can leave a partial skeleton.\n generatedApiNames.push(contentType.singularName);\n await getService('content-types').generateAPI({\n displayName: contentType!.displayName,\n singularName: contentType!.singularName,\n pluralName: contentType!.pluralName,\n kind: contentType!.kind,\n });\n }\n }\n\n if (action === 'update') {\n builder.editContentType({\n ...contentType,\n attributes: contentType.attributes.reduce((acc: any, attr: any) => {\n // NOTE: handle renaming migrations here by comparing attr name & attr.properties.name\n\n if (attr.action === 'delete') {\n return acc;\n }\n\n acc[attr.name] = attr.properties;\n return acc;\n }, {}),\n });\n }\n\n if (action === 'delete') {\n builder.deleteContentType(uid);\n await apiHandler.backup(uid);\n backedUpApiUids.push(uid);\n }\n }\n\n for (const component of components) {\n const { action, uid } = component;\n\n if (action === 'create') {\n builder.createComponentAttributes(\n uid,\n component.attributes.reduce((acc: any, attr: any) => {\n acc[attr.name] = attr.properties;\n return acc;\n }, {})\n );\n }\n\n if (action === 'update') {\n builder.editComponent({\n ...component,\n attributes: component.attributes.reduce((acc: any, attr: any) => {\n if (attr.action === 'delete') {\n return acc;\n }\n\n acc[attr.name] = attr.properties;\n return acc;\n }, {}),\n });\n }\n\n if (action === 'delete') {\n builder.deleteComponent(uid);\n }\n }\n\n // run sanity checks on the schema\n // Relations target existing types\n // Bidirectional relation have their counterpart in the schema\n // Components target existing components\n // Nested components target existing components\n // Dynamic zones target existing components\n\n const schemaFilesWritten = await builder.writeFiles();\n\n if (!schemaFilesWritten) {\n schemaAlreadyRolledBack = true;\n throw new errors.ApplicationError('Invalid schema edition');\n }\n\n for (const uid of APIsToDelete) {\n await apiHandler.clear(uid, { preserveBackup: true });\n }\n\n // Commit the single folder file last. Every preceding filesystem mutation can be restored.\n await getService('content-structure').commitFromUpdate({\n incomingStructure: contentStructure,\n deletedUids,\n });\n } catch (error) {\n await rollbackSchemaMutation({\n builder,\n apiHandler,\n backedUpApiUids,\n generatedApiNames,\n schemaAlreadyRolledBack,\n });\n\n throw error;\n }\n\n // Backup cleanup is deliberately outside the compensating boundary: at this point all\n // user-visible artifacts, including groups.json, have committed.\n for (const error of await finalizeSchemaMutation({ apiHandler, backedUpApiUids })) {\n strapi.log.error(error);\n }\n\n for (const contentType of contentTypes) {\n if (contentType.action === 'delete') {\n strapi.eventHub.emit('content-type.delete', {\n contentType: builder.contentTypes.get(contentType.uid),\n });\n }\n\n if (contentType.action === 'update') {\n strapi.eventHub.emit('content-type.update', {\n contentType: builder.contentTypes.get(contentType.uid),\n });\n }\n\n if (contentType.action === 'create') {\n strapi.eventHub.emit('content-type.create', {\n contentType: builder.contentTypes.get(contentType.uid),\n });\n }\n }\n\n for (const component of components) {\n if (component.action === 'delete') {\n strapi.eventHub.emit('component.delete', {\n component: builder.components.get(component.uid),\n });\n }\n\n if (component.action === 'update') {\n strapi.eventHub.emit('component.update', {\n component: builder.components.get(component.uid),\n });\n }\n\n if (component.action === 'create') {\n strapi.eventHub.emit('component.create', {\n component: builder.components.get(component.uid),\n });\n }\n }\n};\n"],"names":["removeEmptyDefaultsOnUpdates","schema","components","forEach","component","action","attributes","attribute","properties","default","undefined","contentTypes","contentType","removeDeletedUIDTargetFieldsOnUpdates","type","targetField","find","attr","name","formatAttributes","model","getVisibleAttributes","contentTypesUtils","map","key","formatAttribute","targetAttribute","inversedBy","mappedBy","conditions","getSchema","mapValues","uid","options","globalId","pluginOptions","kind","modelName","plugin","collectionName","info","modelType","visible","isContentTypeVisible","restrictRelationsTo","getRestrictRelationsTo","strapi","category","coreContentStructure","get","contentStructure","getCleanedFile","updateSchema","builder","createBuilder","apiHandler","getService","filter","assertCTBOwnedApplicationContentType","upsertedUids","Map","deletedUids","Set","generatedApiNames","backedUpApiUids","schemaAlreadyRolledBack","APIsToDelete","set","add","validateFromUpdate","incomingStructure","createContentType","createComponent","createContentTypeAttributes","reduce","acc","push","singularName","generateAPI","displayName","pluralName","editContentType","deleteContentType","backup","createComponentAttributes","editComponent","deleteComponent","schemaFilesWritten","writeFiles","errors","ApplicationError","clear","preserveBackup","commitFromUpdate","error","rollbackSchemaMutation","finalizeSchemaMutation","log","eventHub","emit"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { contentTypes } from '@strapi/utils';
|
|
1
|
+
import { errors, contentTypes } from '@strapi/utils';
|
|
2
2
|
import { mapValues } from 'lodash/fp';
|
|
3
3
|
import createBuilder from './schema-builder/index.mjs';
|
|
4
|
+
import { rollbackSchemaMutation, finalizeSchemaMutation } from './schema-mutation.mjs';
|
|
4
5
|
import { getService } from '../utils/index.mjs';
|
|
5
|
-
import { getRestrictRelationsTo, isContentTypeVisible } from './content-types.mjs';
|
|
6
|
+
import { getRestrictRelationsTo, isContentTypeVisible, assertCTBOwnedApplicationContentType } from './content-types.mjs';
|
|
6
7
|
|
|
7
8
|
const removeEmptyDefaultsOnUpdates = (schema)=>{
|
|
8
9
|
schema.components.forEach((component)=>{
|
|
@@ -102,7 +103,10 @@ const getSchema = async ()=>{
|
|
|
102
103
|
attributes: formatAttributes(component)
|
|
103
104
|
};
|
|
104
105
|
}, strapi.components);
|
|
106
|
+
const coreContentStructure = strapi.get('content-structure');
|
|
107
|
+
const contentStructure = await coreContentStructure.getCleanedFile();
|
|
105
108
|
return {
|
|
109
|
+
contentStructure,
|
|
106
110
|
contentTypes,
|
|
107
111
|
components
|
|
108
112
|
};
|
|
@@ -110,103 +114,154 @@ const getSchema = async ()=>{
|
|
|
110
114
|
const updateSchema = async (schema)=>{
|
|
111
115
|
const builder = createBuilder();
|
|
112
116
|
const apiHandler = getService('api-handler');
|
|
113
|
-
const { components, contentTypes } = schema;
|
|
117
|
+
const { components, contentTypes, contentStructure } = schema;
|
|
118
|
+
// Reject protected/plugin deletes before builders, API backups, or folder reconciliation can
|
|
119
|
+
// mutate anything. This is the server-side invariant for crafted update requests.
|
|
120
|
+
contentTypes.filter((contentType)=>contentType.action === 'delete').forEach((contentType)=>assertCTBOwnedApplicationContentType(contentType.uid));
|
|
114
121
|
// pre-process data
|
|
115
122
|
removeEmptyDefaultsOnUpdates(schema);
|
|
116
123
|
removeDeletedUIDTargetFieldsOnUpdates(schema);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
const upsertedUids = new Map();
|
|
125
|
+
const deletedUids = new Set();
|
|
126
|
+
const generatedApiNames = [];
|
|
127
|
+
const backedUpApiUids = [];
|
|
128
|
+
let schemaAlreadyRolledBack = false;
|
|
129
|
+
const APIsToDelete = contentTypes.filter((contentType)=>contentType.action === 'delete').map((contentType)=>contentType.uid);
|
|
130
|
+
try {
|
|
131
|
+
for (const contentType of contentTypes){
|
|
132
|
+
if (contentType.action === 'create') {
|
|
133
|
+
upsertedUids.set(contentType.uid, contentType.kind ?? 'collectionType');
|
|
134
|
+
} else if (contentType.action === 'update' && contentType.kind) {
|
|
135
|
+
// A kind switch in the same save must override the registry's stale kind,
|
|
136
|
+
// otherwise a folder assignment into the new section fails validation.
|
|
137
|
+
upsertedUids.set(contentType.uid, contentType.kind);
|
|
138
|
+
} else if (contentType.action === 'delete') {
|
|
139
|
+
deletedUids.add(contentType.uid);
|
|
140
|
+
}
|
|
124
141
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
142
|
+
// Validate folder references before anything is written. This will throw if invalid.
|
|
143
|
+
getService('content-structure').validateFromUpdate({
|
|
144
|
+
incomingStructure: contentStructure,
|
|
145
|
+
upsertedUids,
|
|
146
|
+
deletedUids
|
|
147
|
+
});
|
|
148
|
+
// we pre create empty typesk
|
|
149
|
+
for (const contentType of contentTypes){
|
|
150
|
+
if (contentType.action === 'create') {
|
|
151
|
+
builder.createContentType({
|
|
152
|
+
...contentType,
|
|
153
|
+
attributes: {}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
133
156
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return acc;
|
|
141
|
-
}, {}));
|
|
142
|
-
if (!contentType.plugin) {
|
|
143
|
-
await getService('content-types').generateAPI({
|
|
144
|
-
displayName: contentType.displayName,
|
|
145
|
-
singularName: contentType.singularName,
|
|
146
|
-
pluralName: contentType.pluralName,
|
|
147
|
-
kind: contentType.kind
|
|
157
|
+
// we pre create empty types
|
|
158
|
+
for (const component of components){
|
|
159
|
+
if (component.action === 'create') {
|
|
160
|
+
builder.createComponent({
|
|
161
|
+
...component,
|
|
162
|
+
attributes: {}
|
|
148
163
|
});
|
|
149
164
|
}
|
|
150
165
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
// NOTE: handle renaming migrations here by comparing attr name & attr.properties.name
|
|
156
|
-
if (attr.action === 'delete') {
|
|
157
|
-
return acc;
|
|
158
|
-
}
|
|
166
|
+
for (const contentType of contentTypes){
|
|
167
|
+
const { action, uid } = contentType;
|
|
168
|
+
if (action === 'create') {
|
|
169
|
+
builder.createContentTypeAttributes(uid, contentType.attributes.reduce((acc, attr)=>{
|
|
159
170
|
acc[attr.name] = attr.properties;
|
|
160
171
|
return acc;
|
|
161
|
-
}, {})
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
172
|
+
}, {}));
|
|
173
|
+
if (!contentType.plugin) {
|
|
174
|
+
// Track before generation because a generator failure can leave a partial skeleton.
|
|
175
|
+
generatedApiNames.push(contentType.singularName);
|
|
176
|
+
await getService('content-types').generateAPI({
|
|
177
|
+
displayName: contentType.displayName,
|
|
178
|
+
singularName: contentType.singularName,
|
|
179
|
+
pluralName: contentType.pluralName,
|
|
180
|
+
kind: contentType.kind
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (action === 'update') {
|
|
185
|
+
builder.editContentType({
|
|
186
|
+
...contentType,
|
|
187
|
+
attributes: contentType.attributes.reduce((acc, attr)=>{
|
|
188
|
+
// NOTE: handle renaming migrations here by comparing attr name & attr.properties.name
|
|
189
|
+
if (attr.action === 'delete') {
|
|
190
|
+
return acc;
|
|
191
|
+
}
|
|
192
|
+
acc[attr.name] = attr.properties;
|
|
182
193
|
return acc;
|
|
183
|
-
}
|
|
194
|
+
}, {})
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (action === 'delete') {
|
|
198
|
+
builder.deleteContentType(uid);
|
|
199
|
+
await apiHandler.backup(uid);
|
|
200
|
+
backedUpApiUids.push(uid);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const component of components){
|
|
204
|
+
const { action, uid } = component;
|
|
205
|
+
if (action === 'create') {
|
|
206
|
+
builder.createComponentAttributes(uid, component.attributes.reduce((acc, attr)=>{
|
|
184
207
|
acc[attr.name] = attr.properties;
|
|
185
208
|
return acc;
|
|
186
|
-
}, {})
|
|
187
|
-
}
|
|
209
|
+
}, {}));
|
|
210
|
+
}
|
|
211
|
+
if (action === 'update') {
|
|
212
|
+
builder.editComponent({
|
|
213
|
+
...component,
|
|
214
|
+
attributes: component.attributes.reduce((acc, attr)=>{
|
|
215
|
+
if (attr.action === 'delete') {
|
|
216
|
+
return acc;
|
|
217
|
+
}
|
|
218
|
+
acc[attr.name] = attr.properties;
|
|
219
|
+
return acc;
|
|
220
|
+
}, {})
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (action === 'delete') {
|
|
224
|
+
builder.deleteComponent(uid);
|
|
225
|
+
}
|
|
188
226
|
}
|
|
189
|
-
|
|
190
|
-
|
|
227
|
+
// run sanity checks on the schema
|
|
228
|
+
// Relations target existing types
|
|
229
|
+
// Bidirectional relation have their counterpart in the schema
|
|
230
|
+
// Components target existing components
|
|
231
|
+
// Nested components target existing components
|
|
232
|
+
// Dynamic zones target existing components
|
|
233
|
+
const schemaFilesWritten = await builder.writeFiles();
|
|
234
|
+
if (!schemaFilesWritten) {
|
|
235
|
+
schemaAlreadyRolledBack = true;
|
|
236
|
+
throw new errors.ApplicationError('Invalid schema edition');
|
|
191
237
|
}
|
|
192
|
-
}
|
|
193
|
-
// run sanity checks on the schema
|
|
194
|
-
// Relations target existing types
|
|
195
|
-
// Bidirectional relation have their counterpart in the schema
|
|
196
|
-
// Components target existing components
|
|
197
|
-
// Nested components target existing components
|
|
198
|
-
// Dynamic zones target existing components
|
|
199
|
-
const APIsToDelete = contentTypes.filter((ct)=>ct.action === 'delete').map((ct)=>ct.uid);
|
|
200
|
-
await builder.writeFiles();
|
|
201
|
-
try {
|
|
202
238
|
for (const uid of APIsToDelete){
|
|
203
|
-
await apiHandler.clear(uid
|
|
239
|
+
await apiHandler.clear(uid, {
|
|
240
|
+
preserveBackup: true
|
|
241
|
+
});
|
|
204
242
|
}
|
|
243
|
+
// Commit the single folder file last. Every preceding filesystem mutation can be restored.
|
|
244
|
+
await getService('content-structure').commitFromUpdate({
|
|
245
|
+
incomingStructure: contentStructure,
|
|
246
|
+
deletedUids
|
|
247
|
+
});
|
|
205
248
|
} catch (error) {
|
|
249
|
+
await rollbackSchemaMutation({
|
|
250
|
+
builder,
|
|
251
|
+
apiHandler,
|
|
252
|
+
backedUpApiUids,
|
|
253
|
+
generatedApiNames,
|
|
254
|
+
schemaAlreadyRolledBack
|
|
255
|
+
});
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
// Backup cleanup is deliberately outside the compensating boundary: at this point all
|
|
259
|
+
// user-visible artifacts, including groups.json, have committed.
|
|
260
|
+
for (const error of (await finalizeSchemaMutation({
|
|
261
|
+
apiHandler,
|
|
262
|
+
backedUpApiUids
|
|
263
|
+
}))){
|
|
206
264
|
strapi.log.error(error);
|
|
207
|
-
for (const uid of APIsToDelete){
|
|
208
|
-
await apiHandler.rollback(uid);
|
|
209
|
-
}
|
|
210
265
|
}
|
|
211
266
|
for (const contentType of contentTypes){
|
|
212
267
|
if (contentType.action === 'delete') {
|