@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/README.md
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# strapi-utils
|
|
2
2
|
|
|
3
|
-
[](https://david-dm.org/strapi/strapi-utils)
|
|
6
|
-
[](https://travis-ci.org/strapi/strapi-utils)
|
|
7
|
-
[](https://slack.strapi.io)
|
|
3
|
+
[](https://www.npmjs.org/package/@strapi/utils)
|
|
4
|
+
[](https://www.npmjs.org/package/@strapi/utils)
|
|
8
5
|
|
|
9
6
|
Shared utilities between Strapi packages.
|
|
10
7
|
|
package/lib/build-query.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
//TODO: move to dbal
|
|
3
|
+
// TODO: move to dbal
|
|
4
4
|
|
|
5
5
|
const _ = require('lodash');
|
|
6
6
|
const parseType = require('./parse-type');
|
|
@@ -25,7 +25,7 @@ const getAssociationFromFieldKey = ({ model, field }) => {
|
|
|
25
25
|
const part = fieldParts[i];
|
|
26
26
|
attribute = part;
|
|
27
27
|
|
|
28
|
-
const assoc = tmpModel.associations.find(ast => ast.alias === part);
|
|
28
|
+
const assoc = tmpModel.associations.find((ast) => ast.alias === part);
|
|
29
29
|
|
|
30
30
|
if (assoc) {
|
|
31
31
|
association = assoc;
|
|
@@ -59,7 +59,7 @@ const getAssociationFromFieldKey = ({ model, field }) => {
|
|
|
59
59
|
*/
|
|
60
60
|
const castInput = ({ type, value, operator }) => {
|
|
61
61
|
return Array.isArray(value)
|
|
62
|
-
? value.map(val => castValue({ type, operator, value: val }))
|
|
62
|
+
? value.map((val) => castValue({ type, operator, value: val }))
|
|
63
63
|
: castValue({ type, operator, value });
|
|
64
64
|
};
|
|
65
65
|
|
|
@@ -84,9 +84,7 @@ const castValue = ({ type, value, operator }) => {
|
|
|
84
84
|
const normalizeFieldName = ({ model, field }) => {
|
|
85
85
|
const fieldPath = field.split('.');
|
|
86
86
|
return _.last(fieldPath) === 'id'
|
|
87
|
-
? _.initial(fieldPath)
|
|
88
|
-
.concat(model.primaryKey)
|
|
89
|
-
.join('.')
|
|
87
|
+
? _.initial(fieldPath).concat(model.primaryKey).join('.')
|
|
90
88
|
: fieldPath.join('.');
|
|
91
89
|
};
|
|
92
90
|
|
|
@@ -100,7 +98,7 @@ const hasDeepFilters = ({ where = [], sort = [] }, { minDepth = 1 } = {}) => {
|
|
|
100
98
|
|
|
101
99
|
const hasDeepWhereClauses = where.some(({ field, operator, value }) => {
|
|
102
100
|
if (BOOLEAN_OPERATORS.includes(operator)) {
|
|
103
|
-
return value.some(clauses => hasDeepFilters({ where: clauses }));
|
|
101
|
+
return value.some((clauses) => hasDeepFilters({ where: clauses }));
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
return field.split('.').length > minDepth;
|
|
@@ -114,7 +112,8 @@ const normalizeWhereClauses = (whereClauses, { model }) => {
|
|
|
114
112
|
.filter(({ field, value }) => {
|
|
115
113
|
if (_.isNull(value)) {
|
|
116
114
|
return false;
|
|
117
|
-
}
|
|
115
|
+
}
|
|
116
|
+
if (_.isUndefined(value)) {
|
|
118
117
|
strapi.log.warn(`The value of field: '${field}', in your where filter, is undefined.`);
|
|
119
118
|
return false;
|
|
120
119
|
}
|
|
@@ -125,7 +124,7 @@ const normalizeWhereClauses = (whereClauses, { model }) => {
|
|
|
125
124
|
return {
|
|
126
125
|
field,
|
|
127
126
|
operator,
|
|
128
|
-
value: value.map(clauses => normalizeWhereClauses(clauses, { model })),
|
|
127
|
+
value: value.map((clauses) => normalizeWhereClauses(clauses, { model })),
|
|
129
128
|
};
|
|
130
129
|
}
|
|
131
130
|
|
package/lib/code-generator.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// Using timestamp (milliseconds) to be sure it is unique
|
|
4
4
|
// + converting timestamp to base 36 for better readibility
|
|
5
|
-
const generateTimestampCode = date => {
|
|
5
|
+
const generateTimestampCode = (date) => {
|
|
6
6
|
const referDate = date || new Date();
|
|
7
7
|
|
|
8
8
|
return referDate.getTime().toString(36);
|
package/lib/config.js
CHANGED
|
@@ -62,22 +62,24 @@ const getConfigUrls = (config, forAdminBuild = false) => {
|
|
|
62
62
|
};
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
const getAbsoluteUrl =
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
const getAbsoluteUrl =
|
|
66
|
+
(adminOrServer) =>
|
|
67
|
+
(config, forAdminBuild = false) => {
|
|
68
|
+
const { serverUrl, adminUrl } = getConfigUrls(config, forAdminBuild);
|
|
69
|
+
const url = adminOrServer === 'server' ? serverUrl : adminUrl;
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
if (url.startsWith('http')) {
|
|
72
|
+
return url;
|
|
73
|
+
}
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
const hostname =
|
|
76
|
+
config.get('environment') === 'development' &&
|
|
77
|
+
['127.0.0.1', '0.0.0.0'].includes(config.get('server.host'))
|
|
78
|
+
? 'localhost'
|
|
79
|
+
: config.get('server.host');
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
};
|
|
81
|
+
return `http://${hostname}:${config.get('server.port')}${url}`;
|
|
82
|
+
};
|
|
81
83
|
|
|
82
84
|
module.exports = {
|
|
83
85
|
getConfigUrls,
|
package/lib/content-types.js
CHANGED
|
@@ -32,7 +32,7 @@ const constants = {
|
|
|
32
32
|
COLLECTION_TYPE,
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
const getTimestamps = model => {
|
|
35
|
+
const getTimestamps = (model) => {
|
|
36
36
|
const attributes = [];
|
|
37
37
|
|
|
38
38
|
if (has(CREATED_AT_ATTRIBUTE, model.attributes)) {
|
|
@@ -64,7 +64,7 @@ const isWritableAttribute = (model, attributeName) => {
|
|
|
64
64
|
return getWritableAttributes(model).includes(attributeName);
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const getNonVisibleAttributes = model => {
|
|
67
|
+
const getNonVisibleAttributes = (model) => {
|
|
68
68
|
const nonVisibleAttributes = _.reduce(
|
|
69
69
|
model.attributes,
|
|
70
70
|
(acc, attr, attrName) => (attr.visible === false ? acc.concat(attrName) : acc),
|
|
@@ -74,7 +74,7 @@ const getNonVisibleAttributes = model => {
|
|
|
74
74
|
return _.uniq([ID_ATTRIBUTE, ...getTimestamps(model), ...nonVisibleAttributes]);
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
const getVisibleAttributes = model => {
|
|
77
|
+
const getVisibleAttributes = (model) => {
|
|
78
78
|
return _.difference(_.keys(model.attributes), getNonVisibleAttributes(model));
|
|
79
79
|
};
|
|
80
80
|
|
|
@@ -82,20 +82,20 @@ const isVisibleAttribute = (model, attributeName) => {
|
|
|
82
82
|
return getVisibleAttributes(model).includes(attributeName);
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
const hasDraftAndPublish = model => _.get(model, 'options.draftAndPublish', false) === true;
|
|
85
|
+
const hasDraftAndPublish = (model) => _.get(model, 'options.draftAndPublish', false) === true;
|
|
86
86
|
|
|
87
87
|
const isDraft = (data, model) =>
|
|
88
88
|
hasDraftAndPublish(model) && _.get(data, PUBLISHED_AT_ATTRIBUTE) === null;
|
|
89
89
|
|
|
90
90
|
const isSingleType = ({ kind = COLLECTION_TYPE }) => kind === SINGLE_TYPE;
|
|
91
91
|
const isCollectionType = ({ kind = COLLECTION_TYPE }) => kind === COLLECTION_TYPE;
|
|
92
|
-
const isKind = kind => model => model.kind === kind;
|
|
92
|
+
const isKind = (kind) => (model) => model.kind === kind;
|
|
93
93
|
|
|
94
94
|
const getPrivateAttributes = (model = {}) => {
|
|
95
95
|
return _.union(
|
|
96
96
|
strapi.config.get('api.responses.privateAttributes', []),
|
|
97
97
|
_.get(model, 'options.privateAttributes', []),
|
|
98
|
-
_.keys(_.pickBy(model.attributes, attr => !!attr.private))
|
|
98
|
+
_.keys(_.pickBy(model.attributes, (attr) => !!attr.private))
|
|
99
99
|
);
|
|
100
100
|
};
|
|
101
101
|
|
|
@@ -103,24 +103,34 @@ const isPrivateAttribute = (model = {}, attributeName) => {
|
|
|
103
103
|
return model && model.privateAttributes && model.privateAttributes.includes(attributeName);
|
|
104
104
|
};
|
|
105
105
|
|
|
106
|
-
const isScalarAttribute = attribute => {
|
|
106
|
+
const isScalarAttribute = (attribute) => {
|
|
107
107
|
return !['media', 'component', 'relation', 'dynamiczone'].includes(attribute.type);
|
|
108
108
|
};
|
|
109
|
+
const isMediaAttribute = (attribute) => attribute.type === 'media';
|
|
110
|
+
const isRelationalAttribute = (attribute) => attribute.type === 'relation';
|
|
111
|
+
const isComponentAttribute = (attribute) => ['component', 'dynamiczone'].includes(attribute.type);
|
|
109
112
|
|
|
110
|
-
const
|
|
113
|
+
const getComponentAttributes = (schema) => {
|
|
111
114
|
return _.reduce(
|
|
112
115
|
schema.attributes,
|
|
113
116
|
(acc, attr, attrName) => {
|
|
114
|
-
if (
|
|
117
|
+
if (isComponentAttribute(attr)) acc.push(attrName);
|
|
115
118
|
return acc;
|
|
116
119
|
},
|
|
117
120
|
[]
|
|
118
121
|
);
|
|
119
122
|
};
|
|
120
123
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
const getScalarAttributes = (schema) => {
|
|
125
|
+
return _.reduce(
|
|
126
|
+
schema.attributes,
|
|
127
|
+
(acc, attr, attrName) => {
|
|
128
|
+
if (isScalarAttribute(attr)) acc.push(attrName);
|
|
129
|
+
return acc;
|
|
130
|
+
},
|
|
131
|
+
[]
|
|
132
|
+
);
|
|
133
|
+
};
|
|
124
134
|
|
|
125
135
|
/**
|
|
126
136
|
* Checks if an attribute is of type `type`
|
|
@@ -136,7 +146,7 @@ const isTypedAttribute = (attribute, type) => {
|
|
|
136
146
|
* @param {object} contentType
|
|
137
147
|
* @returns {string}
|
|
138
148
|
*/
|
|
139
|
-
const getContentTypeRoutePrefix = contentType => {
|
|
149
|
+
const getContentTypeRoutePrefix = (contentType) => {
|
|
140
150
|
return isSingleType(contentType)
|
|
141
151
|
? _.kebabCase(contentType.info.singularName)
|
|
142
152
|
: _.kebabCase(contentType.info.pluralName);
|
|
@@ -152,6 +162,7 @@ module.exports = {
|
|
|
152
162
|
isPrivateAttribute,
|
|
153
163
|
constants,
|
|
154
164
|
getNonWritableAttributes,
|
|
165
|
+
getComponentAttributes,
|
|
155
166
|
getScalarAttributes,
|
|
156
167
|
getWritableAttributes,
|
|
157
168
|
isWritableAttribute,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/* eslint-disable max-classes-per-file */
|
|
2
|
+
|
|
1
3
|
'use strict';
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -25,13 +27,13 @@ class InvalidSortError extends Error {
|
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
const validateOrder = order => {
|
|
30
|
+
const validateOrder = (order) => {
|
|
29
31
|
if (!['asc', 'desc'].includes(order.toLocaleLowerCase())) {
|
|
30
32
|
throw new InvalidOrderError();
|
|
31
33
|
}
|
|
32
34
|
};
|
|
33
35
|
|
|
34
|
-
const convertCountQueryParams = countQuery => {
|
|
36
|
+
const convertCountQueryParams = (countQuery) => {
|
|
35
37
|
return parseType({ type: 'boolean', value: countQuery });
|
|
36
38
|
};
|
|
37
39
|
|
|
@@ -39,13 +41,13 @@ const convertCountQueryParams = countQuery => {
|
|
|
39
41
|
* Sort query parser
|
|
40
42
|
* @param {string} sortQuery - ex: id:asc,price:desc
|
|
41
43
|
*/
|
|
42
|
-
const convertSortQueryParams = sortQuery => {
|
|
44
|
+
const convertSortQueryParams = (sortQuery) => {
|
|
43
45
|
if (typeof sortQuery === 'string') {
|
|
44
|
-
return sortQuery.split(',').map(value => convertSingleSortQueryParam(value));
|
|
46
|
+
return sortQuery.split(',').map((value) => convertSingleSortQueryParam(value));
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
if (Array.isArray(sortQuery)) {
|
|
48
|
-
return sortQuery.flatMap(sortValue => convertSortQueryParams(sortValue));
|
|
50
|
+
return sortQuery.flatMap((sortValue) => convertSortQueryParams(sortValue));
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
if (_.isPlainObject(sortQuery)) {
|
|
@@ -55,7 +57,7 @@ const convertSortQueryParams = sortQuery => {
|
|
|
55
57
|
throw new InvalidSortError();
|
|
56
58
|
};
|
|
57
59
|
|
|
58
|
-
const convertSingleSortQueryParam = sortQuery => {
|
|
60
|
+
const convertSingleSortQueryParam = (sortQuery) => {
|
|
59
61
|
// split field and order param with default order to ascending
|
|
60
62
|
const [field, order = 'asc'] = sortQuery.split(':');
|
|
61
63
|
|
|
@@ -68,9 +70,9 @@ const convertSingleSortQueryParam = sortQuery => {
|
|
|
68
70
|
return _.set({}, field, order);
|
|
69
71
|
};
|
|
70
72
|
|
|
71
|
-
const convertNestedSortQueryParam = sortQuery => {
|
|
73
|
+
const convertNestedSortQueryParam = (sortQuery) => {
|
|
72
74
|
const transformedSort = {};
|
|
73
|
-
for (const field
|
|
75
|
+
for (const field of Object.keys(sortQuery)) {
|
|
74
76
|
const order = sortQuery[field];
|
|
75
77
|
|
|
76
78
|
// this is a deep sort
|
|
@@ -89,7 +91,7 @@ const convertNestedSortQueryParam = sortQuery => {
|
|
|
89
91
|
* Start query parser
|
|
90
92
|
* @param {string} startQuery
|
|
91
93
|
*/
|
|
92
|
-
const convertStartQueryParams = startQuery => {
|
|
94
|
+
const convertStartQueryParams = (startQuery) => {
|
|
93
95
|
const startAsANumber = _.toNumber(startQuery);
|
|
94
96
|
|
|
95
97
|
if (!_.isInteger(startAsANumber) || startAsANumber < 0) {
|
|
@@ -103,7 +105,7 @@ const convertStartQueryParams = startQuery => {
|
|
|
103
105
|
* Limit query parser
|
|
104
106
|
* @param {string} limitQuery
|
|
105
107
|
*/
|
|
106
|
-
const convertLimitQueryParams = limitQuery => {
|
|
108
|
+
const convertLimitQueryParams = (limitQuery) => {
|
|
107
109
|
const limitAsANumber = _.toNumber(limitQuery);
|
|
108
110
|
|
|
109
111
|
if (!_.isInteger(limitAsANumber) || (limitAsANumber !== -1 && limitAsANumber < 0)) {
|
|
@@ -130,18 +132,18 @@ const convertPopulateQueryParams = (populate, schema, depth = 0) => {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
if (typeof populate === 'string') {
|
|
133
|
-
return populate.split(',').map(value => _.trim(value));
|
|
135
|
+
return populate.split(',').map((value) => _.trim(value));
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
if (Array.isArray(populate)) {
|
|
137
139
|
// map convert
|
|
138
140
|
return _.uniq(
|
|
139
|
-
populate.flatMap(value => {
|
|
141
|
+
populate.flatMap((value) => {
|
|
140
142
|
if (typeof value !== 'string') {
|
|
141
143
|
throw new InvalidPopulateError();
|
|
142
144
|
}
|
|
143
145
|
|
|
144
|
-
return value.split(',').map(value => _.trim(value));
|
|
146
|
+
return value.split(',').map((value) => _.trim(value));
|
|
145
147
|
})
|
|
146
148
|
);
|
|
147
149
|
}
|
|
@@ -171,8 +173,14 @@ const convertPopulateObject = (populate, schema) => {
|
|
|
171
173
|
// fixed when we'll implement a more accurate way to query them
|
|
172
174
|
if (attribute.type === 'dynamiczone') {
|
|
173
175
|
const populates = attribute.components
|
|
174
|
-
.map(uid => strapi.getModel(uid))
|
|
175
|
-
.map(schema => convertNestedPopulate(subPopulate, schema))
|
|
176
|
+
.map((uid) => strapi.getModel(uid))
|
|
177
|
+
.map((schema) => convertNestedPopulate(subPopulate, schema))
|
|
178
|
+
.map((populate) => (populate === true ? {} : populate)) // cast boolean to empty object to avoid merging issues
|
|
179
|
+
.filter((populate) => populate !== false);
|
|
180
|
+
|
|
181
|
+
if (isEmpty(populates)) {
|
|
182
|
+
return acc;
|
|
183
|
+
}
|
|
176
184
|
|
|
177
185
|
return {
|
|
178
186
|
...acc,
|
|
@@ -201,16 +209,22 @@ const convertPopulateObject = (populate, schema) => {
|
|
|
201
209
|
return acc;
|
|
202
210
|
}
|
|
203
211
|
|
|
212
|
+
const populateObject = convertNestedPopulate(subPopulate, targetSchema);
|
|
213
|
+
|
|
214
|
+
if (!populateObject) {
|
|
215
|
+
return acc;
|
|
216
|
+
}
|
|
217
|
+
|
|
204
218
|
return {
|
|
205
219
|
...acc,
|
|
206
|
-
[key]:
|
|
220
|
+
[key]: populateObject,
|
|
207
221
|
};
|
|
208
222
|
}, {});
|
|
209
223
|
};
|
|
210
224
|
|
|
211
225
|
const convertNestedPopulate = (subPopulate, schema) => {
|
|
212
|
-
if (subPopulate
|
|
213
|
-
return true;
|
|
226
|
+
if (_.isString(subPopulate)) {
|
|
227
|
+
return parseType({ type: 'boolean', value: subPopulate, forceCast: true });
|
|
214
228
|
}
|
|
215
229
|
|
|
216
230
|
if (_.isBoolean(subPopulate)) {
|
|
@@ -255,13 +269,13 @@ const convertFieldsQueryParams = (fields, depth = 0) => {
|
|
|
255
269
|
}
|
|
256
270
|
|
|
257
271
|
if (typeof fields === 'string') {
|
|
258
|
-
const fieldsValues = fields.split(',').map(value => _.trim(value));
|
|
272
|
+
const fieldsValues = fields.split(',').map((value) => _.trim(value));
|
|
259
273
|
return _.uniq(['id', ...fieldsValues]);
|
|
260
274
|
}
|
|
261
275
|
|
|
262
276
|
if (Array.isArray(fields)) {
|
|
263
277
|
// map convert
|
|
264
|
-
const fieldsValues = fields.flatMap(value => convertFieldsQueryParams(value, depth + 1));
|
|
278
|
+
const fieldsValues = fields.flatMap((value) => convertFieldsQueryParams(value, depth + 1));
|
|
265
279
|
return _.uniq(['id', ...fieldsValues]);
|
|
266
280
|
}
|
|
267
281
|
|
|
@@ -290,13 +304,13 @@ const convertAndSanitizeFilters = (filters, schema) => {
|
|
|
290
304
|
return (
|
|
291
305
|
filters
|
|
292
306
|
// Sanitize each filter
|
|
293
|
-
.map(filter => convertAndSanitizeFilters(filter, schema))
|
|
307
|
+
.map((filter) => convertAndSanitizeFilters(filter, schema))
|
|
294
308
|
// Filter out empty filters
|
|
295
|
-
.filter(filter => !isObject(filter) || !isEmpty(filter))
|
|
309
|
+
.filter((filter) => !isObject(filter) || !isEmpty(filter))
|
|
296
310
|
);
|
|
297
311
|
}
|
|
298
312
|
|
|
299
|
-
const removeOperator = operator => delete filters[operator];
|
|
313
|
+
const removeOperator = (operator) => delete filters[operator];
|
|
300
314
|
|
|
301
315
|
// Here, `key` can either be an operator or an attribute name
|
|
302
316
|
for (const [key, value] of Object.entries(filters)) {
|
|
@@ -324,24 +338,23 @@ const convertAndSanitizeFilters = (filters, schema) => {
|
|
|
324
338
|
removeOperator(key);
|
|
325
339
|
}
|
|
326
340
|
|
|
341
|
+
// Password attributes
|
|
342
|
+
else if (attribute.type === 'password') {
|
|
343
|
+
// Always remove password attributes from filters object
|
|
344
|
+
removeOperator(key);
|
|
345
|
+
}
|
|
346
|
+
|
|
327
347
|
// Scalar attributes
|
|
328
348
|
else {
|
|
329
|
-
|
|
330
|
-
if (attribute.type === 'password') {
|
|
331
|
-
removeOperator(key);
|
|
332
|
-
} else {
|
|
333
|
-
filters[key] = convertAndSanitizeFilters(value, schema);
|
|
334
|
-
}
|
|
349
|
+
filters[key] = convertAndSanitizeFilters(value, schema);
|
|
335
350
|
}
|
|
336
351
|
}
|
|
337
352
|
|
|
338
353
|
// Handle operators
|
|
339
|
-
else {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
filters[key] = convertAndSanitizeFilters(value, schema);
|
|
344
|
-
}
|
|
354
|
+
else if (['$null', '$notNull'].includes(key)) {
|
|
355
|
+
filters[key] = parseType({ type: 'boolean', value: filters[key], forceCast: true });
|
|
356
|
+
} else if (isObject(value)) {
|
|
357
|
+
filters[key] = convertAndSanitizeFilters(value, schema);
|
|
345
358
|
}
|
|
346
359
|
|
|
347
360
|
// Remove empty objects & arrays
|
package/lib/env-helper.js
CHANGED
package/lib/errors.js
CHANGED
package/lib/format-yup-error.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const { isEmpty, toPath } = require('lodash/fp');
|
|
4
4
|
|
|
5
|
-
const formatYupInnerError = yupError => ({
|
|
5
|
+
const formatYupInnerError = (yupError) => ({
|
|
6
6
|
path: toPath(yupError.path),
|
|
7
7
|
message: yupError.message,
|
|
8
8
|
name: yupError.name,
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
const formatYupErrors = yupError => ({
|
|
11
|
+
const formatYupErrors = (yupError) => ({
|
|
12
12
|
errors: isEmpty(yupError.inner)
|
|
13
13
|
? [formatYupInnerError(yupError)]
|
|
14
14
|
: yupError.inner.map(formatYupInnerError),
|
package/lib/hooks.js
CHANGED
|
@@ -85,7 +85,7 @@ const createAsyncParallelHook = () => ({
|
|
|
85
85
|
...createHook(),
|
|
86
86
|
|
|
87
87
|
async call(context) {
|
|
88
|
-
const promises = this.getHandlers().map(handler => handler(cloneDeep(context)));
|
|
88
|
+
const promises = this.getHandlers().map((handler) => handler(cloneDeep(context)));
|
|
89
89
|
|
|
90
90
|
return Promise.all(promises);
|
|
91
91
|
},
|
package/lib/object-formatting.js
CHANGED
package/lib/pagination.js
CHANGED
|
@@ -30,10 +30,12 @@ const ensureMinValues = ({ start, limit }) => ({
|
|
|
30
30
|
limit: limit === -1 ? limit : Math.max(limit, 1),
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
const ensureMaxValues =
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const ensureMaxValues =
|
|
34
|
+
(maxLimit = -1) =>
|
|
35
|
+
({ start, limit }) => ({
|
|
36
|
+
start,
|
|
37
|
+
limit: withMaxLimit(limit, maxLimit),
|
|
38
|
+
});
|
|
37
39
|
|
|
38
40
|
// Apply maxLimit as the limit when limit is -1
|
|
39
41
|
const withNoLimit = (pagination, maxLimit = -1) => ({
|
package/lib/parse-multipart.js
CHANGED
package/lib/parse-type.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const dates = require('date-fns');
|
|
5
5
|
|
|
6
|
-
const timeRegex =
|
|
6
|
+
const timeRegex = /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(.[0-9]{1,3})?$/;
|
|
7
7
|
|
|
8
|
-
const parseTime = value => {
|
|
8
|
+
const parseTime = (value) => {
|
|
9
9
|
if (dates.isDate(value)) return dates.format(value, 'HH:mm:ss.SSS');
|
|
10
10
|
|
|
11
11
|
if (typeof value !== 'string') {
|
|
@@ -23,10 +23,10 @@ const parseTime = value => {
|
|
|
23
23
|
return `${hours}:${minutes}:${seconds}.${fractionPart}`;
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const parseDate = value => {
|
|
26
|
+
const parseDate = (value) => {
|
|
27
27
|
if (dates.isDate(value)) return dates.format(value, 'yyyy-MM-dd');
|
|
28
28
|
try {
|
|
29
|
-
|
|
29
|
+
const date = dates.parseISO(value);
|
|
30
30
|
|
|
31
31
|
if (dates.isValid(date)) return dates.format(date, 'yyyy-MM-dd');
|
|
32
32
|
|
|
@@ -36,7 +36,7 @@ const parseDate = value => {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
const parseDateTimeOrTimestamp = value => {
|
|
39
|
+
const parseDateTimeOrTimestamp = (value) => {
|
|
40
40
|
if (dates.isDate(value)) return value;
|
|
41
41
|
try {
|
|
42
42
|
const date = dates.parseISO(value);
|
package/lib/pipe-async.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports =
|
|
4
|
-
|
|
3
|
+
module.exports =
|
|
4
|
+
(...methods) =>
|
|
5
|
+
async (data) => {
|
|
6
|
+
let res = data;
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
for (const method of methods) {
|
|
9
|
+
res = await method(res);
|
|
10
|
+
}
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
+
return res;
|
|
13
|
+
};
|
package/lib/policy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Policies util
|
|
3
3
|
*/
|
|
4
|
+
|
|
4
5
|
'use strict';
|
|
5
6
|
|
|
6
7
|
const _ = require('lodash');
|
|
@@ -9,7 +10,7 @@ const { eq } = require('lodash/fp');
|
|
|
9
10
|
const PLUGIN_PREFIX = 'plugin::';
|
|
10
11
|
const API_PREFIX = 'api::';
|
|
11
12
|
|
|
12
|
-
const parsePolicy = policy => {
|
|
13
|
+
const parsePolicy = (policy) => {
|
|
13
14
|
if (typeof policy === 'string') {
|
|
14
15
|
return { policyName: policy, config: {} };
|
|
15
16
|
}
|
|
@@ -43,7 +44,7 @@ const globalPolicy = ({ method, endpoint, controller, action, plugin }) => {
|
|
|
43
44
|
};
|
|
44
45
|
|
|
45
46
|
const resolvePolicies = (config, { pluginName, apiName } = {}) => {
|
|
46
|
-
return config.map(policyConfig => {
|
|
47
|
+
return config.map((policyConfig) => {
|
|
47
48
|
return {
|
|
48
49
|
handler: getPolicy(policyConfig, { pluginName, apiName }),
|
|
49
50
|
config: policyConfig.config || {},
|
|
@@ -87,10 +88,10 @@ const getPolicy = (policyConfig, { pluginName, apiName } = {}) => {
|
|
|
87
88
|
return policy.handler;
|
|
88
89
|
};
|
|
89
90
|
|
|
90
|
-
const createPolicy = options => {
|
|
91
|
+
const createPolicy = (options) => {
|
|
91
92
|
const { name = 'unnamed', validator, handler } = options;
|
|
92
93
|
|
|
93
|
-
const wrappedValidator = config => {
|
|
94
|
+
const wrappedValidator = (config) => {
|
|
94
95
|
if (validator) {
|
|
95
96
|
try {
|
|
96
97
|
validator(config);
|
package/lib/print-value.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Code copied from the yup library (https://github.com/jquense/yup)
|
|
4
4
|
// https://github.com/jquense/yup/blob/2778b88bdacd5260d593c6468793da2e77daf21f/src/util/printValue.ts
|
|
5
5
|
|
|
6
|
-
const toString = Object.prototype
|
|
6
|
+
const { toString } = Object.prototype;
|
|
7
7
|
const errorToString = Error.prototype.toString;
|
|
8
8
|
const regExpToString = RegExp.prototype.toString;
|
|
9
9
|
const symbolToString = typeof Symbol !== 'undefined' ? Symbol.prototype.toString : () => '';
|
|
@@ -13,34 +13,34 @@ const SYMBOL_REGEXP = /^Symbol\((.*)\)(.*)$/;
|
|
|
13
13
|
function printNumber(val) {
|
|
14
14
|
if (val != +val) return 'NaN';
|
|
15
15
|
const isNegativeZero = val === 0 && 1 / val < 0;
|
|
16
|
-
return isNegativeZero ? '-0' :
|
|
16
|
+
return isNegativeZero ? '-0' : `${val}`;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
function printSimpleValue(val, quoteStrings = false) {
|
|
20
|
-
if (val == null || val === true || val === false) return
|
|
20
|
+
if (val == null || val === true || val === false) return `${val}`;
|
|
21
21
|
|
|
22
22
|
const typeOf = typeof val;
|
|
23
23
|
if (typeOf === 'number') return printNumber(val);
|
|
24
24
|
if (typeOf === 'string') return quoteStrings ? `"${val}"` : val;
|
|
25
|
-
if (typeOf === 'function') return
|
|
25
|
+
if (typeOf === 'function') return `[Function ${val.name || 'anonymous'}]`;
|
|
26
26
|
if (typeOf === 'symbol') return symbolToString.call(val).replace(SYMBOL_REGEXP, 'Symbol($1)');
|
|
27
27
|
|
|
28
28
|
const tag = toString.call(val).slice(8, -1);
|
|
29
|
-
if (tag === 'Date') return isNaN(val.getTime()) ?
|
|
30
|
-
if (tag === 'Error' || val instanceof Error) return
|
|
29
|
+
if (tag === 'Date') return Number.isNaN(val.getTime()) ? `${val}` : val.toISOString(val);
|
|
30
|
+
if (tag === 'Error' || val instanceof Error) return `[${errorToString.call(val)}]`;
|
|
31
31
|
if (tag === 'RegExp') return regExpToString.call(val);
|
|
32
32
|
|
|
33
33
|
return null;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function printValue(value, quoteStrings) {
|
|
37
|
-
|
|
37
|
+
const result = printSimpleValue(value, quoteStrings);
|
|
38
38
|
if (result !== null) return result;
|
|
39
39
|
|
|
40
40
|
return JSON.stringify(
|
|
41
41
|
value,
|
|
42
|
-
function(key, value) {
|
|
43
|
-
|
|
42
|
+
function replacer(key, value) {
|
|
43
|
+
const result = printSimpleValue(this[key], quoteStrings);
|
|
44
44
|
if (result !== null) return result;
|
|
45
45
|
return value;
|
|
46
46
|
},
|