@strapi/utils 4.3.4 → 4.3.7
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/README.md +2 -5
- package/lib/build-query.js +8 -9
- package/lib/code-generator.js +1 -1
- package/lib/config.js +15 -13
- package/lib/content-types.js +24 -13
- package/lib/convert-query-params.js +48 -35
- package/lib/env-helper.js +1 -1
- package/lib/errors.js +2 -0
- package/lib/format-yup-error.js +2 -2
- package/lib/hooks.js +1 -1
- package/lib/object-formatting.js +1 -1
- package/lib/pagination.js +6 -4
- package/lib/parse-multipart.js +1 -1
- package/lib/parse-type.js +5 -5
- package/lib/pipe-async.js +9 -7
- package/lib/policy.js +5 -4
- package/lib/print-value.js +9 -9
- package/lib/provider-factory.js +1 -1
- package/lib/relations.js +2 -2
- package/lib/sanitize/index.js +4 -4
- package/lib/sanitize/visitors/allowed-fields.js +60 -58
- package/lib/sanitize/visitors/remove-restricted-relations.js +45 -43
- package/lib/sanitize/visitors/restricted-fields.js +25 -24
- package/lib/set-creator-fields.js +11 -9
- package/lib/string-formatting.js +10 -9
- package/lib/template-configuration.js +1 -1
- package/lib/traverse-entity.js +4 -4
- package/lib/validators.js +36 -27
- package/package.json +3 -3
package/lib/provider-factory.js
CHANGED
package/lib/relations.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const MANY_RELATIONS = ['oneToMany', 'manyToMany'];
|
|
4
4
|
|
|
5
|
-
const getRelationalFields = contentType => {
|
|
6
|
-
return Object.keys(contentType.attributes).filter(attributeName => {
|
|
5
|
+
const getRelationalFields = (contentType) => {
|
|
6
|
+
return Object.keys(contentType.attributes).filter((attributeName) => {
|
|
7
7
|
return contentType.attributes[attributeName].type === 'relation';
|
|
8
8
|
});
|
|
9
9
|
};
|
package/lib/sanitize/index.js
CHANGED
|
@@ -13,7 +13,7 @@ module.exports = {
|
|
|
13
13
|
contentAPI: {
|
|
14
14
|
input(data, schema, { auth } = {}) {
|
|
15
15
|
if (isArray(data)) {
|
|
16
|
-
return Promise.all(data.map(entry => this.input(entry, schema, { auth })));
|
|
16
|
+
return Promise.all(data.map((entry) => this.input(entry, schema, { auth })));
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
const nonWritableAttributes = getNonWritableAttributes(schema);
|
|
@@ -31,14 +31,14 @@ module.exports = {
|
|
|
31
31
|
// Apply sanitizers from registry if exists
|
|
32
32
|
strapi.sanitizers
|
|
33
33
|
.get('content-api.input')
|
|
34
|
-
.forEach(sanitizer => transforms.push(sanitizer(schema)));
|
|
34
|
+
.forEach((sanitizer) => transforms.push(sanitizer(schema)));
|
|
35
35
|
|
|
36
36
|
return pipeAsync(...transforms)(data);
|
|
37
37
|
},
|
|
38
38
|
|
|
39
39
|
output(data, schema, { auth } = {}) {
|
|
40
40
|
if (isArray(data)) {
|
|
41
|
-
return Promise.all(data.map(entry => this.output(entry, schema, { auth })));
|
|
41
|
+
return Promise.all(data.map((entry) => this.output(entry, schema, { auth })));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const transforms = [sanitizers.defaultSanitizeOutput(schema)];
|
|
@@ -50,7 +50,7 @@ module.exports = {
|
|
|
50
50
|
// Apply sanitizers from registry if exists
|
|
51
51
|
strapi.sanitizers
|
|
52
52
|
.get('content-api.output')
|
|
53
|
-
.forEach(sanitizer => transforms.push(sanitizer(schema)));
|
|
53
|
+
.forEach((sanitizer) => transforms.push(sanitizer(schema)));
|
|
54
54
|
|
|
55
55
|
return pipeAsync(...transforms)(data);
|
|
56
56
|
},
|
|
@@ -2,68 +2,70 @@
|
|
|
2
2
|
|
|
3
3
|
const { isArray, toPath } = require('lodash/fp');
|
|
4
4
|
|
|
5
|
-
module.exports =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
module.exports =
|
|
6
|
+
(allowedFields = null) =>
|
|
7
|
+
({ key, path }, { remove }) => {
|
|
8
|
+
// All fields are allowed
|
|
9
|
+
if (allowedFields === null) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// Ignore invalid formats
|
|
14
|
+
if (!isArray(allowedFields)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
const containedPaths = getContainedPaths(path);
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Tells if the current path should be kept or not based
|
|
22
|
+
* on the success of the check functions for any of the allowed paths.
|
|
23
|
+
*
|
|
24
|
+
* The check functions are defined as follow:
|
|
25
|
+
*
|
|
26
|
+
* `containedPaths.includes(p)`
|
|
27
|
+
* @example
|
|
28
|
+
* ```js
|
|
29
|
+
* const path = 'foo.bar.field';
|
|
30
|
+
* const p = 'foo.bar';
|
|
31
|
+
* // it should match
|
|
32
|
+
*
|
|
33
|
+
* const path = 'foo.bar.field';
|
|
34
|
+
* const p = 'bar.foo';
|
|
35
|
+
* // it shouldn't match
|
|
36
|
+
*
|
|
37
|
+
* const path = 'foo.bar';
|
|
38
|
+
* const p = 'foo.bar.field';
|
|
39
|
+
* // it should match but isn't handled by this check
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* `p.startsWith(`${path}.`)`
|
|
43
|
+
* @example
|
|
44
|
+
* ```js
|
|
45
|
+
* const path = 'foo.bar';
|
|
46
|
+
* const p = 'foo.bar.field';
|
|
47
|
+
* // it should match
|
|
48
|
+
*
|
|
49
|
+
* const path = 'foo.bar.field';
|
|
50
|
+
* const p = 'bar.foo';
|
|
51
|
+
* // it shouldn't match
|
|
52
|
+
*
|
|
53
|
+
* const path = 'foo.bar.field';
|
|
54
|
+
* const p = 'foo.bar';
|
|
55
|
+
* // it should match but isn't handled by this check
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
const isPathAllowed = allowedFields.some(
|
|
59
|
+
(p) => containedPaths.includes(p) || p.startsWith(`${path}.`)
|
|
60
|
+
);
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
if (isPathAllowed) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
};
|
|
66
|
+
// Remove otherwise
|
|
67
|
+
remove(key);
|
|
68
|
+
};
|
|
67
69
|
|
|
68
70
|
/**
|
|
69
71
|
* Retrieve the list of allowed paths based on the given path
|
|
@@ -83,7 +85,7 @@ module.exports = (allowedFields = null) => ({ key, path }, { remove }) => {
|
|
|
83
85
|
* // ['foo', 'foo.bar', 'foo.bar.field']
|
|
84
86
|
* ```
|
|
85
87
|
*/
|
|
86
|
-
const getContainedPaths = path => {
|
|
88
|
+
const getContainedPaths = (path) => {
|
|
87
89
|
const parts = toPath(path);
|
|
88
90
|
|
|
89
91
|
return parts.reduce((acc, value, index, list) => {
|
|
@@ -4,62 +4,64 @@ const ACTIONS_TO_VERIFY = ['find'];
|
|
|
4
4
|
|
|
5
5
|
const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = require('../../content-types').constants;
|
|
6
6
|
|
|
7
|
-
module.exports =
|
|
8
|
-
|
|
7
|
+
module.exports =
|
|
8
|
+
(auth) =>
|
|
9
|
+
async ({ data, key, attribute, schema }, { remove, set }) => {
|
|
10
|
+
const isRelation = attribute.type === 'relation';
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
if (!isRelation) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
const handleMorphRelation = async () => {
|
|
17
|
+
const newMorphValue = [];
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
for (const element of data[key]) {
|
|
20
|
+
const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);
|
|
21
|
+
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
if (isAllowed) {
|
|
24
|
+
newMorphValue.push(element);
|
|
25
|
+
}
|
|
23
26
|
}
|
|
24
|
-
}
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// If the new value is empty, remove the relation completely
|
|
29
|
+
if (newMorphValue.length === 0) {
|
|
30
|
+
remove(key);
|
|
31
|
+
} else {
|
|
32
|
+
set(key, newMorphValue);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
const handleRegularRelation = async () => {
|
|
37
|
+
const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
const isAllowed = await hasAccessToSomeScopes(scopes, auth);
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
// If the authenticated user don't have access to any of the scopes, then remove the field
|
|
42
|
+
if (!isAllowed) {
|
|
43
|
+
remove(key);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');
|
|
48
|
+
const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
// Polymorphic relations
|
|
51
|
+
if (isMorphRelation) {
|
|
52
|
+
await handleMorphRelation();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
// Creator relations
|
|
57
|
+
if (isCreatorRelation && schema.options.populateCreatorFields) {
|
|
58
|
+
// do nothing
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
};
|
|
62
|
+
// Regular relations
|
|
63
|
+
await handleRegularRelation();
|
|
64
|
+
};
|
|
63
65
|
|
|
64
66
|
const hasAccessToSomeScopes = async (scopes, auth) => {
|
|
65
67
|
for (const scope of scopes) {
|
|
@@ -2,30 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
const { isArray } = require('lodash/fp');
|
|
4
4
|
|
|
5
|
-
module.exports =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
module.exports =
|
|
6
|
+
(restrictedFields = null) =>
|
|
7
|
+
({ key, path }, { remove }) => {
|
|
8
|
+
// Remove all fields
|
|
9
|
+
if (restrictedFields === null) {
|
|
10
|
+
remove(key);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
// Ignore invalid formats
|
|
15
|
+
if (!isArray(restrictedFields)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
// Remove if an exact match was found
|
|
20
|
+
if (restrictedFields.includes(path)) {
|
|
21
|
+
remove(key);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
};
|
|
25
|
+
// Remove nested matches
|
|
26
|
+
const isRestrictedNested = restrictedFields.some((allowedPath) =>
|
|
27
|
+
path.startsWith(`${allowedPath}.`)
|
|
28
|
+
);
|
|
29
|
+
if (isRestrictedNested) {
|
|
30
|
+
remove(key);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
const { assign, assoc } = require('lodash/fp');
|
|
4
4
|
const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = require('./content-types').constants;
|
|
5
5
|
|
|
6
|
-
module.exports =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
module.exports =
|
|
7
|
+
({ user, isEdition = false }) =>
|
|
8
|
+
(data) => {
|
|
9
|
+
if (isEdition) {
|
|
10
|
+
return assoc(UPDATED_BY_ATTRIBUTE, user.id, data);
|
|
11
|
+
}
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
13
|
+
return assign(data, {
|
|
14
|
+
[CREATED_BY_ATTRIBUTE]: user.id,
|
|
15
|
+
[UPDATED_BY_ATTRIBUTE]: user.id,
|
|
16
|
+
});
|
|
17
|
+
};
|
package/lib/string-formatting.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
|
|
2
3
|
const _ = require('lodash');
|
|
3
4
|
const { trimChars, trimCharsEnd, trimCharsStart } = require('lodash/fp');
|
|
4
5
|
const slugify = require('@sindresorhus/slugify');
|
|
@@ -6,9 +7,9 @@ const { kebabCase } = require('lodash');
|
|
|
6
7
|
|
|
7
8
|
const nameToSlug = (name, options = { separator: '-' }) => slugify(name, options);
|
|
8
9
|
|
|
9
|
-
const nameToCollectionName = name => slugify(name, { separator: '_' });
|
|
10
|
+
const nameToCollectionName = (name) => slugify(name, { separator: '_' });
|
|
10
11
|
|
|
11
|
-
const toRegressedEnumValue = value =>
|
|
12
|
+
const toRegressedEnumValue = (value) =>
|
|
12
13
|
slugify(value, {
|
|
13
14
|
decamelize: false,
|
|
14
15
|
lowercase: false,
|
|
@@ -16,14 +17,14 @@ const toRegressedEnumValue = value =>
|
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
const getCommonBeginning = (...strings) =>
|
|
19
|
-
_.takeWhile(strings[0], (char, index) => strings.every(string => string[index] === char)).join(
|
|
20
|
+
_.takeWhile(strings[0], (char, index) => strings.every((string) => string[index] === char)).join(
|
|
20
21
|
''
|
|
21
22
|
);
|
|
22
23
|
|
|
23
24
|
const getCommonPath = (...paths) => {
|
|
24
|
-
const [segments, ...otherSegments] = paths.map(it => _.split(it, '/'));
|
|
25
|
+
const [segments, ...otherSegments] = paths.map((it) => _.split(it, '/'));
|
|
25
26
|
return _.join(
|
|
26
|
-
_.takeWhile(segments, (str, index) => otherSegments.every(it => it[index] === str)),
|
|
27
|
+
_.takeWhile(segments, (str, index) => otherSegments.every((it) => it[index] === str)),
|
|
27
28
|
'/'
|
|
28
29
|
);
|
|
29
30
|
};
|
|
@@ -42,9 +43,9 @@ const escapeQuery = (query, charsToEscape, escapeChar = '\\') => {
|
|
|
42
43
|
|
|
43
44
|
const stringIncludes = (arr, val) => arr.map(String).includes(String(val));
|
|
44
45
|
const stringEquals = (a, b) => String(a) === String(b);
|
|
45
|
-
const isCamelCase = value => /^[a-z][a-zA-Z0-9]+$/.test(value);
|
|
46
|
-
const isKebabCase = value => /^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/.test(value);
|
|
47
|
-
const startsWithANumber = value => /^[0-9]/.test(value);
|
|
46
|
+
const isCamelCase = (value) => /^[a-z][a-zA-Z0-9]+$/.test(value);
|
|
47
|
+
const isKebabCase = (value) => /^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/.test(value);
|
|
48
|
+
const startsWithANumber = (value) => /^[0-9]/.test(value);
|
|
48
49
|
|
|
49
50
|
const joinBy = (joint, ...args) => {
|
|
50
51
|
const trim = trimChars(joint);
|
|
@@ -59,7 +60,7 @@ const joinBy = (joint, ...args) => {
|
|
|
59
60
|
}, '');
|
|
60
61
|
};
|
|
61
62
|
|
|
62
|
-
const toKebabCase = value => kebabCase(value);
|
|
63
|
+
const toKebabCase = (value) => kebabCase(value);
|
|
63
64
|
|
|
64
65
|
module.exports = {
|
|
65
66
|
nameToSlug,
|
|
@@ -19,7 +19,7 @@ const templateConfiguration = (obj, configPath = '') => {
|
|
|
19
19
|
!excludeConfigPaths.includes(configPath.substr(1)) &&
|
|
20
20
|
obj[key].match(regex) !== null
|
|
21
21
|
) {
|
|
22
|
-
// eslint-disable-next-line prefer-template
|
|
22
|
+
// eslint-disable-next-line prefer-template, no-eval
|
|
23
23
|
acc[key] = eval('`' + obj[key] + '`');
|
|
24
24
|
} else {
|
|
25
25
|
acc[key] = obj[key];
|
package/lib/traverse-entity.js
CHANGED
|
@@ -46,7 +46,7 @@ const traverseEntity = async (visitor, options, entity) => {
|
|
|
46
46
|
if (isRelation) {
|
|
47
47
|
const isMorphRelation = attribute.relation.toLowerCase().startsWith('morph');
|
|
48
48
|
|
|
49
|
-
const traverseTarget = entry => {
|
|
49
|
+
const traverseTarget = (entry) => {
|
|
50
50
|
// Handle polymorphic relationships
|
|
51
51
|
const targetSchemaUID = isMorphRelation ? entry.__type : attribute.target;
|
|
52
52
|
const targetSchema = strapi.getModel(targetSchemaUID);
|
|
@@ -63,7 +63,7 @@ const traverseEntity = async (visitor, options, entity) => {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
if (isMedia) {
|
|
66
|
-
const traverseTarget = entry => {
|
|
66
|
+
const traverseTarget = (entry) => {
|
|
67
67
|
const targetSchemaUID = 'plugin::upload.file';
|
|
68
68
|
const targetSchema = strapi.getModel(targetSchemaUID);
|
|
69
69
|
|
|
@@ -82,7 +82,7 @@ const traverseEntity = async (visitor, options, entity) => {
|
|
|
82
82
|
const targetSchema = strapi.getModel(attribute.component);
|
|
83
83
|
const traverseOptions = { schema: targetSchema, path: newPath };
|
|
84
84
|
|
|
85
|
-
const traverseComponent = entry => traverseEntity(visitor, traverseOptions, entry);
|
|
85
|
+
const traverseComponent = (entry) => traverseEntity(visitor, traverseOptions, entry);
|
|
86
86
|
|
|
87
87
|
copy[key] = isArray(value)
|
|
88
88
|
? await Promise.all(value.map(traverseComponent))
|
|
@@ -90,7 +90,7 @@ const traverseEntity = async (visitor, options, entity) => {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
if (isDynamicZone && isArray(value)) {
|
|
93
|
-
const visitDynamicZoneEntry = entry => {
|
|
93
|
+
const visitDynamicZoneEntry = (entry) => {
|
|
94
94
|
const targetSchema = strapi.getModel(entry.__component);
|
|
95
95
|
const traverseOptions = { schema: targetSchema, path: newPath };
|
|
96
96
|
|
package/lib/validators.js
CHANGED
|
@@ -9,34 +9,38 @@ const printValue = require('./print-value');
|
|
|
9
9
|
|
|
10
10
|
const MixedSchemaType = yup.MixedSchema;
|
|
11
11
|
|
|
12
|
-
const isNotNilTest = value => !_.isNil(value);
|
|
12
|
+
const isNotNilTest = (value) => !_.isNil(value);
|
|
13
13
|
|
|
14
14
|
function isNotNill(msg = '${path} must be defined.') {
|
|
15
15
|
return this.test('defined', msg, isNotNilTest);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const isNotNullTest = value => !_.isNull(value);
|
|
18
|
+
const isNotNullTest = (value) => !_.isNull(value);
|
|
19
19
|
function isNotNull(msg = '${path} cannot be null.') {
|
|
20
20
|
return this.test('defined', msg, isNotNullTest);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function isFunction(message = '${path} is not a function') {
|
|
24
|
-
return this.test(
|
|
24
|
+
return this.test(
|
|
25
|
+
'is a function',
|
|
26
|
+
message,
|
|
27
|
+
(value) => _.isUndefined(value) || _.isFunction(value)
|
|
28
|
+
);
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
function isCamelCase(message = '${path} is not in camel case (anExampleOfCamelCase)') {
|
|
28
|
-
return this.test('is in camelCase', message, value => utils.isCamelCase(value));
|
|
32
|
+
return this.test('is in camelCase', message, (value) => utils.isCamelCase(value));
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
function isKebabCase(message = '${path} is not in kebab case (an-example-of-kebab-case)') {
|
|
32
|
-
return this.test('is in kebab-case', message, value => utils.isKebabCase(value));
|
|
36
|
+
return this.test('is in kebab-case', message, (value) => utils.isKebabCase(value));
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
function onlyContainsFunctions(message = '${path} contains values that are not functions') {
|
|
36
40
|
return this.test(
|
|
37
41
|
'only contains functions',
|
|
38
42
|
message,
|
|
39
|
-
value => _.isUndefined(value) || (value && Object.values(value).every(_.isFunction))
|
|
43
|
+
(value) => _.isUndefined(value) || (value && Object.values(value).every(_.isFunction))
|
|
40
44
|
);
|
|
41
45
|
}
|
|
42
46
|
|
|
@@ -65,33 +69,38 @@ const handleYupError = (error, errorMessage) => {
|
|
|
65
69
|
|
|
66
70
|
const defaultValidationParam = { strict: true, abortEarly: false };
|
|
67
71
|
|
|
68
|
-
const validateYupSchema =
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
const validateYupSchema =
|
|
73
|
+
(schema, options = {}) =>
|
|
74
|
+
async (body, errorMessage) => {
|
|
75
|
+
try {
|
|
76
|
+
const optionsWithDefaults = defaults(defaultValidationParam, options);
|
|
77
|
+
return await schema.validate(body, optionsWithDefaults);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
handleYupError(e, errorMessage);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const validateYupSchemaSync =
|
|
84
|
+
(schema, options = {}) =>
|
|
85
|
+
(body, errorMessage) => {
|
|
86
|
+
try {
|
|
87
|
+
const optionsWithDefaults = defaults(defaultValidationParam, options);
|
|
88
|
+
return schema.validateSync(body, optionsWithDefaults);
|
|
89
|
+
} catch (e) {
|
|
90
|
+
handleYupError(e, errorMessage);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
85
93
|
|
|
86
94
|
// Temporary fix of this issue : https://github.com/jquense/yup/issues/616
|
|
87
95
|
yup.setLocale({
|
|
88
96
|
mixed: {
|
|
89
97
|
notType({ path, type, value, originalValue }) {
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
const isCast = originalValue != null && originalValue !== value;
|
|
99
|
+
const msg =
|
|
92
100
|
`${path} must be a \`${type}\` type, ` +
|
|
93
|
-
`but the final value was: \`${printValue(value, true)}
|
|
94
|
-
|
|
101
|
+
`but the final value was: \`${printValue(value, true)}\`${
|
|
102
|
+
isCast ? ` (cast from the value \`${printValue(originalValue, true)}\`).` : '.'
|
|
103
|
+
}`;
|
|
95
104
|
|
|
96
105
|
/* Remove comment that is not supposed to be seen by the enduser
|
|
97
106
|
if (value === null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/utils",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.7",
|
|
4
4
|
"description": "Shared utilities for the Strapi packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@sindresorhus/slugify": "1.1.0",
|
|
39
|
-
"date-fns": "2.
|
|
39
|
+
"date-fns": "2.29.2",
|
|
40
40
|
"http-errors": "1.8.1",
|
|
41
41
|
"lodash": "4.17.21",
|
|
42
42
|
"yup": "0.32.9"
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"node": ">=14.19.1 <=16.x.x",
|
|
46
46
|
"npm": ">=6.0.0"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "73f523b98322cea8992c72977b94a73a624d2e79"
|
|
49
49
|
}
|