@strapi/utils 4.0.7 → 4.0.8

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.
@@ -104,13 +104,21 @@ const isPrivateAttribute = (model = {}, attributeName) => {
104
104
  };
105
105
 
106
106
  const isScalarAttribute = attribute => {
107
- return !['component', 'relation', 'dynamiczone'].includes(attribute.type);
107
+ return !['media', 'component', 'relation', 'dynamiczone'].includes(attribute.type);
108
108
  };
109
109
 
110
- const isMediaAttribute = attr => {
111
- return attr.type === 'media';
110
+ const getScalarAttributes = schema => {
111
+ return _.reduce(
112
+ schema.attributes,
113
+ (acc, attr, attrName) => {
114
+ if (isScalarAttribute(attr)) acc.push(attrName);
115
+ return acc;
116
+ },
117
+ []
118
+ );
112
119
  };
113
120
 
121
+ const isMediaAttribute = attribute => attribute.type === 'media';
114
122
  const isRelationalAttribute = attribute => attribute.type === 'relation';
115
123
  const isComponentAttribute = attribute => ['component', 'dynamiczone'].includes(attribute.type);
116
124
 
@@ -144,6 +152,7 @@ module.exports = {
144
152
  isPrivateAttribute,
145
153
  constants,
146
154
  getNonWritableAttributes,
155
+ getScalarAttributes,
147
156
  getWritableAttributes,
148
157
  isWritableAttribute,
149
158
  getNonVisibleAttributes,
@@ -4,7 +4,7 @@
4
4
  * Converts the standard Strapi REST query params to a more usable format for querying
5
5
  * You can read more here: https://docs.strapi.io/developer-docs/latest/developer-resources/database-apis-reference/rest-api.html#filters
6
6
  */
7
- const { has } = require('lodash/fp');
7
+ const { has, isEmpty, isObject, cloneDeep, get } = require('lodash/fp');
8
8
  const _ = require('lodash');
9
9
  const parseType = require('./parse-type');
10
10
  const contentTypesUtils = require('./content-types');
@@ -218,7 +218,90 @@ const convertFieldsQueryParams = (fields, depth = 0) => {
218
218
  throw new Error('Invalid fields parameter. Expected a string or an array of strings');
219
219
  };
220
220
 
221
- const convertFiltersQueryParams = filters => filters;
221
+ const convertFiltersQueryParams = (filters, schema) => {
222
+ // Filters need to be either an array or an object
223
+ // Here we're only checking for 'object' type since typeof [] => object and typeof {} => object
224
+ if (!isObject(filters)) {
225
+ throw new Error('The filters parameter must be an object or an array');
226
+ }
227
+
228
+ // Don't mutate the original object
229
+ const filtersCopy = cloneDeep(filters);
230
+
231
+ return convertAndSanitizeFilters(filtersCopy, schema);
232
+ };
233
+
234
+ const convertAndSanitizeFilters = (filters, schema) => {
235
+ if (!isObject(filters)) {
236
+ return filters;
237
+ }
238
+
239
+ if (Array.isArray(filters)) {
240
+ return (
241
+ filters
242
+ // Sanitize each filter
243
+ .map(filter => convertAndSanitizeFilters(filter, schema))
244
+ // Filter out empty filters
245
+ .filter(filter => !isObject(filter) || !isEmpty(filter))
246
+ );
247
+ }
248
+
249
+ const removeOperator = operator => delete filters[operator];
250
+
251
+ // Here, `key` can either be an operator or an attribute name
252
+ for (const [key, value] of Object.entries(filters)) {
253
+ const attribute = get('key', schema.attributes);
254
+
255
+ // Handle attributes
256
+ if (attribute) {
257
+ // Relations
258
+ if (attribute.type === 'relation') {
259
+ filters[key] = convertAndSanitizeFilters(value, strapi.getModel(attribute.target));
260
+ }
261
+
262
+ // Components
263
+ else if (attribute.type === 'component') {
264
+ filters[key] = convertAndSanitizeFilters(value, strapi.getModel(attribute.component));
265
+ }
266
+
267
+ // Media
268
+ else if (attribute.type === 'media') {
269
+ filters[key] = convertAndSanitizeFilters(value, strapi.getModel('plugin::upload.file'));
270
+ }
271
+
272
+ // Dynamic Zones
273
+ else if (attribute.type === 'dynamiczone') {
274
+ removeOperator(key);
275
+ }
276
+
277
+ // Scalar attributes
278
+ else {
279
+ // Always remove password attributes from filters object
280
+ if (attribute.type === 'password') {
281
+ removeOperator(key);
282
+ } else {
283
+ filters[key] = convertAndSanitizeFilters(value);
284
+ }
285
+ }
286
+ }
287
+
288
+ // Handle operators
289
+ else {
290
+ if (['$null', '$notNull'].includes(key)) {
291
+ filters[key] = parseType({ type: 'boolean', value: filters[key], forceCast: true });
292
+ } else if (isObject(value)) {
293
+ filters[key] = convertAndSanitizeFilters(value, schema);
294
+ }
295
+ }
296
+
297
+ // Remove empty objects & arrays
298
+ if (isObject(filters[key]) && isEmpty(filters[key])) {
299
+ removeOperator(key);
300
+ }
301
+ }
302
+
303
+ return filters;
304
+ };
222
305
 
223
306
  const convertPublicationStateParams = (type, params = {}, query = {}) => {
224
307
  if (!type) {
package/lib/parse-type.js CHANGED
@@ -57,7 +57,7 @@ const parseDateTimeOrTimestamp = value => {
57
57
  * @param {string} options.type - type of the atribute
58
58
  * @param {*} options.value - value tu cast
59
59
  */
60
- const parseType = ({ type, value }) => {
60
+ const parseType = ({ type, value, forceCast = false }) => {
61
61
  switch (type) {
62
62
  case 'boolean': {
63
63
  if (typeof value === 'boolean') return value;
@@ -70,6 +70,10 @@ const parseType = ({ type, value }) => {
70
70
  return false;
71
71
  }
72
72
 
73
+ if (forceCast) {
74
+ return Boolean(value);
75
+ }
76
+
73
77
  throw new Error('Invalid boolean input. Expected "t","1","true","false","0","f"');
74
78
  }
75
79
  case 'integer':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/utils",
3
- "version": "4.0.7",
3
+ "version": "4.0.8",
4
4
  "description": "Shared utilities for the Strapi packages",
5
5
  "keywords": [
6
6
  "strapi",
@@ -45,5 +45,5 @@
45
45
  "node": ">=12.22.0 <=16.x.x",
46
46
  "npm": ">=6.0.0"
47
47
  },
48
- "gitHead": "af0cba8c5b2ba7b371523e8f55413ef0fce98e1e"
48
+ "gitHead": "669bb2f0440d3b21a23c8d665fdba98bd3d8cc71"
49
49
  }