@strapi/strapi 4.5.0-beta.0 → 4.5.1
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/lib/Strapi.js +1 -1
- package/lib/commands/builders/admin.js +1 -0
- package/lib/core/app-configuration/load-config-file.js +1 -3
- package/lib/core/loaders/apis.js +1 -2
- package/lib/core/loaders/middlewares.js +1 -2
- package/lib/core/loaders/policies.js +1 -2
- package/lib/core/loaders/src-index.js +1 -3
- package/lib/core/registries/custom-fields.js +20 -2
- package/lib/load/load-files.js +1 -1
- package/lib/middlewares/favicon.js +16 -3
- package/lib/services/entity-service/components.js +16 -26
- package/lib/services/entity-service/index.js +4 -2
- package/lib/services/entity-validator/index.js +129 -2
- package/lib/services/server/middleware.js +2 -1
- package/lib/utils/index.js +0 -2
- package/package.json +16 -16
- package/lib/utils/import-default.js +0 -10
package/lib/Strapi.js
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const fs = require('fs');
|
|
5
|
-
const { templateConfiguration, env } = require('@strapi/utils');
|
|
6
|
-
|
|
7
|
-
const importDefault = require('../../utils/import-default');
|
|
5
|
+
const { templateConfiguration, env, importDefault } = require('@strapi/utils');
|
|
8
6
|
|
|
9
7
|
const loadJsFile = (file) => {
|
|
10
8
|
try {
|
package/lib/core/loaders/apis.js
CHANGED
|
@@ -4,8 +4,7 @@ const { join, extname, basename } = require('path');
|
|
|
4
4
|
const { existsSync } = require('fs-extra');
|
|
5
5
|
const _ = require('lodash');
|
|
6
6
|
const fse = require('fs-extra');
|
|
7
|
-
const { isKebabCase } = require('@strapi/utils');
|
|
8
|
-
const { importDefault } = require('../../utils');
|
|
7
|
+
const { isKebabCase, importDefault } = require('@strapi/utils');
|
|
9
8
|
|
|
10
9
|
const DEFAULT_CONTENT_TYPE = {
|
|
11
10
|
schema: {},
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { join, extname, basename } = require('path');
|
|
4
4
|
const fse = require('fs-extra');
|
|
5
|
-
|
|
6
|
-
const { importDefault } = require('../../utils');
|
|
5
|
+
const { importDefault } = require('@strapi/utils');
|
|
7
6
|
|
|
8
7
|
// TODO:: allow folders with index.js inside for bigger policies
|
|
9
8
|
module.exports = async function loadMiddlewares(strapi) {
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { join, extname, basename } = require('path');
|
|
4
4
|
const fse = require('fs-extra');
|
|
5
|
-
|
|
6
|
-
const { importDefault } = require('../../utils');
|
|
5
|
+
const { importDefault } = require('@strapi/utils');
|
|
7
6
|
|
|
8
7
|
// TODO:: allow folders with index.js inside for bigger policies
|
|
9
8
|
module.exports = async function loadPolicies(strapi) {
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { resolve } = require('path');
|
|
4
4
|
const { statSync, existsSync } = require('fs');
|
|
5
|
-
const { yup } = require('@strapi/utils');
|
|
6
|
-
|
|
7
|
-
const { importDefault } = require('../../utils');
|
|
5
|
+
const { yup, importDefault } = require('@strapi/utils');
|
|
8
6
|
|
|
9
7
|
const srcSchema = yup
|
|
10
8
|
.object()
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { has } = require('lodash/fp');
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
const ALLOWED_TYPES = [
|
|
6
|
+
'biginteger',
|
|
7
|
+
'boolean',
|
|
8
|
+
'date',
|
|
9
|
+
'datetime',
|
|
10
|
+
'decimal',
|
|
11
|
+
'email',
|
|
12
|
+
'enumeration',
|
|
13
|
+
'float',
|
|
14
|
+
'integer',
|
|
15
|
+
'json',
|
|
16
|
+
'password',
|
|
17
|
+
'richtext',
|
|
18
|
+
'string',
|
|
19
|
+
'text',
|
|
20
|
+
'time',
|
|
21
|
+
'uid',
|
|
22
|
+
];
|
|
5
23
|
|
|
6
24
|
const customFieldsRegistry = (strapi) => {
|
|
7
25
|
const customFields = {};
|
|
@@ -27,7 +45,7 @@ const customFieldsRegistry = (strapi) => {
|
|
|
27
45
|
}
|
|
28
46
|
|
|
29
47
|
const { name, plugin, type } = cf;
|
|
30
|
-
if (!
|
|
48
|
+
if (!ALLOWED_TYPES.includes(type)) {
|
|
31
49
|
throw new Error(
|
|
32
50
|
`Custom field type: '${type}' is not a valid Strapi type or it can't be used with a Custom Field`
|
|
33
51
|
);
|
package/lib/load/load-files.js
CHANGED
|
@@ -4,7 +4,7 @@ const path = require('path');
|
|
|
4
4
|
const _ = require('lodash');
|
|
5
5
|
const fse = require('fs-extra');
|
|
6
6
|
|
|
7
|
-
const { importDefault } = require('
|
|
7
|
+
const { importDefault } = require('@strapi/utils');
|
|
8
8
|
const glob = require('./glob');
|
|
9
9
|
const filePathToPath = require('./filepath-to-prop-path');
|
|
10
10
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { existsSync } = require('fs');
|
|
3
4
|
const { resolve } = require('path');
|
|
4
5
|
const { defaultsDeep } = require('lodash/fp');
|
|
5
6
|
const favicon = require('koa-favicon');
|
|
6
7
|
|
|
7
8
|
const defaults = {
|
|
8
|
-
path: 'favicon.
|
|
9
|
+
path: 'favicon.png',
|
|
9
10
|
maxAge: 86400000,
|
|
10
11
|
};
|
|
11
12
|
|
|
@@ -13,7 +14,19 @@ const defaults = {
|
|
|
13
14
|
* @type {import('./').MiddlewareFactory}
|
|
14
15
|
*/
|
|
15
16
|
module.exports = (config, { strapi }) => {
|
|
16
|
-
const { maxAge, path:
|
|
17
|
+
const { maxAge, path: faviconDefaultPath } = defaultsDeep(defaults, config);
|
|
18
|
+
const { root: appRoot } = strapi.dirs.app;
|
|
19
|
+
let faviconPath = faviconDefaultPath;
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
/** TODO (v5): Updating the favicon to use a png caused
|
|
22
|
+
* https://github.com/strapi/strapi/issues/14693
|
|
23
|
+
*
|
|
24
|
+
* This check ensures backwards compatibility until
|
|
25
|
+
* the next major version
|
|
26
|
+
*/
|
|
27
|
+
if (!existsSync(resolve(appRoot, faviconPath))) {
|
|
28
|
+
faviconPath = 'favicon.ico';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return favicon(resolve(appRoot, faviconPath), { maxAge });
|
|
19
32
|
};
|
|
@@ -267,44 +267,34 @@ const deleteOldDZComponents = async (uid, entityToUpdate, attributeName, dynamic
|
|
|
267
267
|
}
|
|
268
268
|
};
|
|
269
269
|
|
|
270
|
-
const deleteComponents = async (uid, entityToDelete) => {
|
|
270
|
+
const deleteComponents = async (uid, entityToDelete, { loadComponents = true } = {}) => {
|
|
271
271
|
const { attributes = {} } = strapi.getModel(uid);
|
|
272
272
|
|
|
273
273
|
for (const attributeName of Object.keys(attributes)) {
|
|
274
274
|
const attribute = attributes[attributeName];
|
|
275
275
|
|
|
276
|
-
if (attribute.type === 'component') {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const value =
|
|
281
|
-
entityToDelete[attributeName] ||
|
|
282
|
-
(await strapi.query(uid).load(entityToDelete, attributeName));
|
|
283
|
-
|
|
284
|
-
if (!value) {
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (Array.isArray(value)) {
|
|
289
|
-
await Promise.all(value.map((subValue) => deleteComponent(componentUID, subValue)));
|
|
276
|
+
if (attribute.type === 'component' || attribute.type === 'dynamiczone') {
|
|
277
|
+
let value;
|
|
278
|
+
if (loadComponents) {
|
|
279
|
+
value = await strapi.query(uid).load(entityToDelete, attributeName);
|
|
290
280
|
} else {
|
|
291
|
-
|
|
281
|
+
value = entityToDelete[attributeName];
|
|
292
282
|
}
|
|
293
283
|
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (attribute.type === 'dynamiczone') {
|
|
298
|
-
const value =
|
|
299
|
-
entityToDelete[attributeName] ||
|
|
300
|
-
(await strapi.query(uid).load(entityToDelete, attributeName));
|
|
301
|
-
|
|
302
284
|
if (!value) {
|
|
303
285
|
continue;
|
|
304
286
|
}
|
|
305
287
|
|
|
306
|
-
if (
|
|
307
|
-
|
|
288
|
+
if (attribute.type === 'component') {
|
|
289
|
+
const { component: componentUID } = attribute;
|
|
290
|
+
await Promise.all(
|
|
291
|
+
_.castArray(value).map((subValue) => deleteComponent(componentUID, subValue))
|
|
292
|
+
);
|
|
293
|
+
} else {
|
|
294
|
+
// delete dynamic zone components
|
|
295
|
+
await Promise.all(
|
|
296
|
+
_.castArray(value).map((subValue) => deleteComponent(subValue.__component, subValue))
|
|
297
|
+
);
|
|
308
298
|
}
|
|
309
299
|
|
|
310
300
|
continue;
|
|
@@ -219,7 +219,7 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
219
219
|
const componentsToDelete = await getComponents(uid, entityToDelete);
|
|
220
220
|
|
|
221
221
|
await db.query(uid).delete({ where: { id: entityToDelete.id } });
|
|
222
|
-
await deleteComponents(uid, {
|
|
222
|
+
await deleteComponents(uid, componentsToDelete, { loadComponents: false });
|
|
223
223
|
|
|
224
224
|
await this.emitEvent(uid, ENTRY_DELETE, entityToDelete);
|
|
225
225
|
|
|
@@ -244,7 +244,9 @@ const createDefaultImplementation = ({ strapi, db, eventHub, entityValidator })
|
|
|
244
244
|
);
|
|
245
245
|
|
|
246
246
|
const deletedEntities = await db.query(uid).deleteMany(query);
|
|
247
|
-
await Promise.all(
|
|
247
|
+
await Promise.all(
|
|
248
|
+
componentsToDelete.map((compos) => deleteComponents(uid, compos, { loadComponents: false }))
|
|
249
|
+
);
|
|
248
250
|
|
|
249
251
|
// Trigger webhooks. One for each entity
|
|
250
252
|
await Promise.all(entitiesToDelete.map((entity) => this.emitEvent(uid, ENTRY_DELETE, entity)));
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
-
const {
|
|
8
|
+
const { uniqBy, castArray, isNil } = require('lodash');
|
|
9
|
+
const { has, assoc, prop, isObject, isEmpty, merge } = require('lodash/fp');
|
|
9
10
|
const strapiUtils = require('@strapi/utils');
|
|
10
11
|
const validators = require('./validators');
|
|
11
12
|
|
|
@@ -222,10 +223,136 @@ const createValidateEntity =
|
|
|
222
223
|
entity,
|
|
223
224
|
},
|
|
224
225
|
{ isDraft }
|
|
225
|
-
)
|
|
226
|
+
)
|
|
227
|
+
.test('relations-test', 'check that all relations exist', async function (data) {
|
|
228
|
+
try {
|
|
229
|
+
await checkRelationsExist(buildRelationsStore({ uid: model.uid, data }));
|
|
230
|
+
} catch (e) {
|
|
231
|
+
return this.createError({
|
|
232
|
+
path: this.path,
|
|
233
|
+
message: e.message,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return true;
|
|
237
|
+
})
|
|
238
|
+
.required();
|
|
239
|
+
|
|
226
240
|
return validateYupSchema(validator, { strict: false, abortEarly: false })(data);
|
|
227
241
|
};
|
|
228
242
|
|
|
243
|
+
/**
|
|
244
|
+
* Builds an object containing all the media and relations being associated with an entity
|
|
245
|
+
* @param {String} uid of the model
|
|
246
|
+
* @param {Object} data
|
|
247
|
+
* @returns {Object}
|
|
248
|
+
*/
|
|
249
|
+
const buildRelationsStore = ({ uid, data }) => {
|
|
250
|
+
if (isEmpty(data)) {
|
|
251
|
+
return {};
|
|
252
|
+
}
|
|
253
|
+
const currentModel = strapi.getModel(uid);
|
|
254
|
+
|
|
255
|
+
return Object.keys(currentModel.attributes).reduce((result, attributeName) => {
|
|
256
|
+
const attribute = currentModel.attributes[attributeName];
|
|
257
|
+
const value = data[attributeName];
|
|
258
|
+
|
|
259
|
+
if (isNil(value)) {
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
switch (attribute.type) {
|
|
264
|
+
case 'relation':
|
|
265
|
+
case 'media': {
|
|
266
|
+
if (attribute.relation === 'morphToMany' || attribute.relation === 'morphToOne') {
|
|
267
|
+
// TODO: handle polymorphic relations
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const target = attribute.type === 'media' ? 'plugin::upload.file' : attribute.target;
|
|
272
|
+
// As there are multiple formats supported for associating relations
|
|
273
|
+
// with an entity, the value here can be an: array, object or number.
|
|
274
|
+
let source;
|
|
275
|
+
if (Array.isArray(value)) {
|
|
276
|
+
source = value;
|
|
277
|
+
} else if (isObject(value)) {
|
|
278
|
+
source = value.connect ?? value.set ?? [];
|
|
279
|
+
} else {
|
|
280
|
+
source = castArray(value);
|
|
281
|
+
}
|
|
282
|
+
const idArray = source.map((v) => ({ id: v.id || v }));
|
|
283
|
+
|
|
284
|
+
// Update the relationStore to keep track of all associations being made
|
|
285
|
+
// with relations and media.
|
|
286
|
+
result[target] = result[target] || [];
|
|
287
|
+
result[target].push(...idArray);
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
case 'component': {
|
|
291
|
+
return castArray(value).reduce(
|
|
292
|
+
(relationsStore, componentValue) =>
|
|
293
|
+
merge(
|
|
294
|
+
relationsStore,
|
|
295
|
+
buildRelationsStore({
|
|
296
|
+
uid: attribute.component,
|
|
297
|
+
data: componentValue,
|
|
298
|
+
})
|
|
299
|
+
),
|
|
300
|
+
result
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
case 'dynamiczone': {
|
|
304
|
+
return value.reduce(
|
|
305
|
+
(relationsStore, dzValue) =>
|
|
306
|
+
merge(
|
|
307
|
+
relationsStore,
|
|
308
|
+
buildRelationsStore({
|
|
309
|
+
uid: dzValue.__component,
|
|
310
|
+
data: dzValue,
|
|
311
|
+
})
|
|
312
|
+
),
|
|
313
|
+
result
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
default:
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return result;
|
|
321
|
+
}, {});
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Iterate through the relations store and validates that every relation or media
|
|
326
|
+
* mentioned exists
|
|
327
|
+
*/
|
|
328
|
+
const checkRelationsExist = async (relationsStore = {}) => {
|
|
329
|
+
const promises = [];
|
|
330
|
+
|
|
331
|
+
for (const [key, value] of Object.entries(relationsStore)) {
|
|
332
|
+
const evaluate = async () => {
|
|
333
|
+
const uniqueValues = uniqBy(value, `id`);
|
|
334
|
+
const count = await strapi.db.query(key).count({
|
|
335
|
+
where: {
|
|
336
|
+
id: {
|
|
337
|
+
$in: uniqueValues.map((v) => v.id),
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
if (count !== uniqueValues.length) {
|
|
343
|
+
throw new ValidationError(
|
|
344
|
+
`${
|
|
345
|
+
uniqueValues.length - count
|
|
346
|
+
} relation(s) of type ${key} associated with this entity do not exist`
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
promises.push(evaluate());
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return Promise.all(promises);
|
|
354
|
+
};
|
|
355
|
+
|
|
229
356
|
module.exports = {
|
|
230
357
|
validateEntityCreation: createValidateEntity('creation'),
|
|
231
358
|
validateEntityUpdate: createValidateEntity('update'),
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { propOr, isArray, isNil } = require('lodash/fp');
|
|
5
|
+
const { importDefault } = require('@strapi/utils');
|
|
5
6
|
|
|
6
7
|
const getMiddlewareConfig = propOr([], 'config.middlewares');
|
|
7
8
|
|
|
@@ -119,7 +120,7 @@ const resolveCustomMiddleware = (resolve, strapi) => {
|
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
try {
|
|
122
|
-
return
|
|
123
|
+
return importDefault(modulePath);
|
|
123
124
|
} catch (err) {
|
|
124
125
|
throw new Error(`Could not load middleware "${modulePath}".`);
|
|
125
126
|
}
|
package/lib/utils/index.js
CHANGED
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
const openBrowser = require('./open-browser');
|
|
4
4
|
const isInitialized = require('./is-initialized');
|
|
5
5
|
const getDirs = require('./get-dirs');
|
|
6
|
-
const importDefault = require('./import-default');
|
|
7
6
|
|
|
8
7
|
module.exports = {
|
|
9
8
|
isInitialized,
|
|
10
9
|
openBrowser,
|
|
11
10
|
getDirs,
|
|
12
|
-
importDefault,
|
|
13
11
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/strapi",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "An open source headless CMS solution to create and manage your own API. It provides a powerful dashboard and features to make your life easier. Databases supported: MySQL, MariaDB, PostgreSQL, SQLite",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -78,25 +78,25 @@
|
|
|
78
78
|
"test:unit": "jest --verbose"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@koa/cors": "3.4.
|
|
81
|
+
"@koa/cors": "3.4.3",
|
|
82
82
|
"@koa/router": "10.1.1",
|
|
83
|
-
"@strapi/admin": "4.5.
|
|
84
|
-
"@strapi/database": "4.5.
|
|
85
|
-
"@strapi/generate-new": "4.5.
|
|
86
|
-
"@strapi/generators": "4.5.
|
|
87
|
-
"@strapi/logger": "4.5.
|
|
88
|
-
"@strapi/permissions": "4.5.
|
|
89
|
-
"@strapi/plugin-content-manager": "4.5.
|
|
90
|
-
"@strapi/plugin-content-type-builder": "4.5.
|
|
91
|
-
"@strapi/plugin-email": "4.5.
|
|
92
|
-
"@strapi/plugin-upload": "4.5.
|
|
93
|
-
"@strapi/typescript-utils": "4.5.
|
|
94
|
-
"@strapi/utils": "4.5.
|
|
83
|
+
"@strapi/admin": "4.5.1",
|
|
84
|
+
"@strapi/database": "4.5.1",
|
|
85
|
+
"@strapi/generate-new": "4.5.1",
|
|
86
|
+
"@strapi/generators": "4.5.1",
|
|
87
|
+
"@strapi/logger": "4.5.1",
|
|
88
|
+
"@strapi/permissions": "4.5.1",
|
|
89
|
+
"@strapi/plugin-content-manager": "4.5.1",
|
|
90
|
+
"@strapi/plugin-content-type-builder": "4.5.1",
|
|
91
|
+
"@strapi/plugin-email": "4.5.1",
|
|
92
|
+
"@strapi/plugin-upload": "4.5.1",
|
|
93
|
+
"@strapi/typescript-utils": "4.5.1",
|
|
94
|
+
"@strapi/utils": "4.5.1",
|
|
95
95
|
"bcryptjs": "2.4.3",
|
|
96
96
|
"boxen": "5.1.2",
|
|
97
97
|
"chalk": "4.1.2",
|
|
98
98
|
"chokidar": "3.5.2",
|
|
99
|
-
"ci-info": "3.
|
|
99
|
+
"ci-info": "3.5.0",
|
|
100
100
|
"cli-table3": "0.6.2",
|
|
101
101
|
"commander": "8.2.0",
|
|
102
102
|
"configstore": "5.0.1",
|
|
@@ -140,5 +140,5 @@
|
|
|
140
140
|
"node": ">=14.19.1 <=18.x.x",
|
|
141
141
|
"npm": ">=6.0.0"
|
|
142
142
|
},
|
|
143
|
-
"gitHead": "
|
|
143
|
+
"gitHead": "8c20ea2b5c5a115b78454086ea270dcd59b06004"
|
|
144
144
|
}
|