@webiny/api-headless-cms-ddb 0.0.0-mt-3 → 0.0.0-unstable.5e7233243f

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.
Files changed (70) hide show
  1. package/definitions/entry.d.ts +2 -1
  2. package/definitions/entry.js +5 -2
  3. package/definitions/entry.js.map +1 -0
  4. package/definitions/group.d.ts +2 -1
  5. package/definitions/group.js +2 -2
  6. package/definitions/group.js.map +1 -0
  7. package/definitions/model.d.ts +2 -1
  8. package/definitions/model.js +2 -2
  9. package/definitions/model.js.map +1 -0
  10. package/definitions/settings.d.ts +2 -1
  11. package/definitions/settings.js +2 -2
  12. package/definitions/settings.js.map +1 -0
  13. package/definitions/system.d.ts +2 -1
  14. package/definitions/system.js +2 -2
  15. package/definitions/system.js.map +1 -0
  16. package/definitions/table.d.ts +2 -1
  17. package/definitions/table.js.map +1 -0
  18. package/dynamoDb/index.d.ts +1 -1
  19. package/dynamoDb/index.js +6 -10
  20. package/dynamoDb/index.js.map +1 -0
  21. package/dynamoDb/path/plainObject.d.ts +2 -3
  22. package/dynamoDb/path/plainObject.js +28 -21
  23. package/dynamoDb/path/plainObject.js.map +1 -0
  24. package/dynamoDb/storage/date.d.ts +2 -3
  25. package/dynamoDb/storage/date.js +79 -45
  26. package/dynamoDb/storage/date.js.map +1 -0
  27. package/dynamoDb/storage/longText.d.ts +7 -4
  28. package/dynamoDb/storage/longText.js +71 -53
  29. package/dynamoDb/storage/longText.js.map +1 -0
  30. package/dynamoDb/storage/richText.d.ts +2 -8
  31. package/dynamoDb/storage/richText.js +69 -66
  32. package/dynamoDb/storage/richText.js.map +1 -0
  33. package/dynamoDb/transformValue/datetime.d.ts +4 -2
  34. package/dynamoDb/transformValue/datetime.js +31 -26
  35. package/dynamoDb/transformValue/datetime.js.map +1 -0
  36. package/index.js +20 -19
  37. package/index.js.map +1 -0
  38. package/operations/entry/dataLoaders.d.ts +12 -8
  39. package/operations/entry/dataLoaders.js +26 -6
  40. package/operations/entry/dataLoaders.js.map +1 -0
  41. package/operations/entry/index.d.ts +2 -2
  42. package/operations/entry/index.js +264 -103
  43. package/operations/entry/index.js.map +1 -0
  44. package/operations/entry/keys.js +11 -0
  45. package/operations/entry/keys.js.map +1 -0
  46. package/operations/entry/systemFields.js +18 -0
  47. package/operations/entry/systemFields.js.map +1 -0
  48. package/operations/entry/utils.d.ts +10 -5
  49. package/operations/entry/utils.js +373 -95
  50. package/operations/entry/utils.js.map +1 -0
  51. package/operations/group/index.d.ts +3 -2
  52. package/operations/group/index.js +3 -5
  53. package/operations/group/index.js.map +1 -0
  54. package/operations/model/index.d.ts +3 -2
  55. package/operations/model/index.js +15 -10
  56. package/operations/model/index.js.map +1 -0
  57. package/operations/settings/index.d.ts +3 -2
  58. package/operations/settings/index.js +3 -5
  59. package/operations/settings/index.js.map +1 -0
  60. package/operations/system/index.d.ts +3 -2
  61. package/operations/system/index.js +3 -5
  62. package/operations/system/index.js.map +1 -0
  63. package/package.json +23 -23
  64. package/plugins/CmsEntryFieldFilterPathPlugin.d.ts +22 -0
  65. package/plugins/CmsEntryFieldFilterPathPlugin.js +55 -0
  66. package/plugins/CmsEntryFieldFilterPathPlugin.js.map +1 -0
  67. package/types.d.ts +1 -32
  68. package/types.js.map +1 -0
  69. package/dynamoDb/path/ref.d.ts +0 -3
  70. package/dynamoDb/path/ref.js +0 -27
@@ -7,9 +7,11 @@ Object.defineProperty(exports, "__esModule", {
7
7
  });
8
8
  exports.sortEntryItems = exports.filterItems = exports.buildModelFields = void 0;
9
9
 
10
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
+
10
12
  var _error = _interopRequireDefault(require("@webiny/error"));
11
13
 
12
- var _lodash = _interopRequireDefault(require("lodash.sortby"));
14
+ var _sortBy = _interopRequireDefault(require("lodash/sortBy"));
13
15
 
14
16
  var _dotProp = _interopRequireDefault(require("dot-prop"));
15
17
 
@@ -17,11 +19,22 @@ var _systemFields = require("./systemFields");
17
19
 
18
20
  var _ValueFilterPlugin = require("@webiny/db-dynamodb/plugins/definitions/ValueFilterPlugin");
19
21
 
22
+ var _CmsEntryFieldFilterPathPlugin = require("../../plugins/CmsEntryFieldFilterPathPlugin");
23
+
24
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
25
+
26
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
27
+
20
28
  const VALUES_ATTRIBUTE = "values";
21
29
 
22
30
  const extractWhereParams = key => {
23
31
  const result = key.split("_");
24
32
  const fieldId = result.shift();
33
+
34
+ if (!fieldId) {
35
+ return null;
36
+ }
37
+
25
38
  const rawOp = result.length === 0 ? "eq" : result.join("_");
26
39
  /**
27
40
  * When rawOp is not, it means it is equal negated so just return that.
@@ -52,6 +65,63 @@ const transformValue = (value, transform) => {
52
65
  return transform(value);
53
66
  };
54
67
 
68
+ const getFilterPlugin = params => {
69
+ const {
70
+ plugins,
71
+ operation
72
+ } = params;
73
+ const plugin = plugins[operation];
74
+
75
+ if (plugin) {
76
+ return plugin;
77
+ }
78
+
79
+ throw new _error.default(`There is no filter plugin for operation "${operation}".`, "FILTER_PLUGIN_ERROR", {
80
+ operation
81
+ });
82
+ };
83
+
84
+ const createValuePath = params => {
85
+ const {
86
+ field,
87
+ plugins,
88
+ index
89
+ } = params;
90
+ const {
91
+ fieldId
92
+ } = field;
93
+ const valuePathPlugin = plugins[field.type];
94
+ const basePath = _systemFields.systemFields[fieldId] ? "" : `${VALUES_ATTRIBUTE}.`;
95
+
96
+ if (!valuePathPlugin || valuePathPlugin.canUse(field) === false) {
97
+ return `${basePath}${fieldId}`;
98
+ }
99
+
100
+ const path = valuePathPlugin.createPath({
101
+ field,
102
+ index
103
+ });
104
+ return `${basePath}${path}`;
105
+ };
106
+
107
+ const isObjectFiltering = params => {
108
+ const {
109
+ value
110
+ } = params;
111
+
112
+ if (!value) {
113
+ return false;
114
+ } else if (Array.isArray(value) === true) {
115
+ return false;
116
+ } else if (value instanceof Date || !!value.toISOString) {
117
+ return false;
118
+ } else if (typeof value !== "object") {
119
+ return false;
120
+ }
121
+
122
+ return true;
123
+ };
124
+
55
125
  const createFilters = params => {
56
126
  const {
57
127
  where,
@@ -70,15 +140,33 @@ const createFilters = params => {
70
140
  });
71
141
  const valuePathPlugins = getMappedPlugins({
72
142
  plugins,
73
- type: "cms-field-filter-path",
143
+ type: _CmsEntryFieldFilterPathPlugin.CmsEntryFieldFilterPathPlugin.type,
74
144
  property: "fieldType"
75
145
  });
76
- return Object.keys(where).map(key => {
146
+ const filters = [];
147
+
148
+ for (const key in where) {
149
+ if (where.hasOwnProperty(key) === false) {
150
+ continue;
151
+ }
152
+
153
+ const value = where[key];
154
+
155
+ if (value === undefined) {
156
+ continue;
157
+ }
158
+
159
+ const whereParams = extractWhereParams(key);
160
+
161
+ if (!whereParams) {
162
+ continue;
163
+ }
164
+
77
165
  const {
78
166
  fieldId,
79
167
  operation,
80
168
  negate
81
- } = extractWhereParams(key);
169
+ } = whereParams;
82
170
  const field = fields[fieldId];
83
171
 
84
172
  if (!field) {
@@ -88,33 +176,6 @@ const createFilters = params => {
88
176
  }
89
177
 
90
178
  const transformValuePlugin = transformValuePlugins[field.def.type];
91
- const valuePathPlugin = valuePathPlugins[field.def.type];
92
- let targetValuePath;
93
- /**
94
- * add the base path if field is not a system field
95
- * pathPlugin should not know about that
96
- */
97
-
98
- const basePath = _systemFields.systemFields[fieldId] ? "" : `${VALUES_ATTRIBUTE}.`;
99
-
100
- if (valuePathPlugin) {
101
- targetValuePath = valuePathPlugin.createPath({
102
- field: field.def
103
- });
104
- } else if (_systemFields.systemFields[fieldId]) {
105
- targetValuePath = fieldId;
106
- } else {
107
- targetValuePath = field.def.fieldId;
108
- }
109
-
110
- const valuePath = `${basePath}${targetValuePath}`;
111
- const filterPlugin = filterPlugins[operation];
112
-
113
- if (!filterPlugin) {
114
- throw new _error.default(`There is no filter plugin for operation "${operation}".`, "FILTER_PLUGIN_ERROR", {
115
- operation
116
- });
117
- }
118
179
 
119
180
  const transformValueCallable = value => {
120
181
  if (!transformValuePlugin) {
@@ -127,42 +188,232 @@ const createFilters = params => {
127
188
  });
128
189
  };
129
190
 
130
- return {
191
+ const objectFilteringParams = {
192
+ key,
193
+ value,
194
+ field: field.def
195
+ };
196
+
197
+ if (isObjectFiltering(objectFilteringParams)) {
198
+ const propertyFilters = Object.keys(value);
199
+
200
+ if (propertyFilters.length === 0) {
201
+ continue;
202
+ }
203
+
204
+ for (const propertyFilter of propertyFilters) {
205
+ const whereParams = extractWhereParams(propertyFilter);
206
+
207
+ if (!whereParams) {
208
+ continue;
209
+ }
210
+
211
+ const {
212
+ fieldId: propertyId,
213
+ operation: propertyOperation,
214
+ negate: propertyNegate
215
+ } = whereParams;
216
+ const filterPlugin = getFilterPlugin({
217
+ plugins: filterPlugins,
218
+ operation: propertyOperation
219
+ });
220
+ const basePath = createValuePath({
221
+ field: field.def,
222
+ plugins: valuePathPlugins
223
+ });
224
+ const multiValuesPath = field.def.multipleValues ? "%s." : "";
225
+ filters.push({
226
+ fieldId,
227
+ path: `${basePath}.${multiValuesPath}${propertyId}`,
228
+ filterPlugin,
229
+ negate: propertyNegate,
230
+ compareValue: transformValue(value[propertyFilter], transformValueCallable),
231
+ transformValue: transformValueCallable
232
+ });
233
+ }
234
+
235
+ continue;
236
+ }
237
+
238
+ const filterPlugin = getFilterPlugin({
239
+ plugins: filterPlugins,
240
+ operation
241
+ });
242
+ filters.push({
131
243
  fieldId,
132
- path: valuePath,
244
+ path: createValuePath({
245
+ field: field.def,
246
+ plugins: valuePathPlugins
247
+ }),
133
248
  filterPlugin,
134
249
  negate,
135
- compareValue: transformValue(where[key], transformValueCallable),
250
+ compareValue: transformValue(value, transformValueCallable),
136
251
  transformValue: transformValueCallable
137
- };
252
+ });
253
+ }
254
+
255
+ return filters;
256
+ };
257
+ /**
258
+ * In case filter field is not multiple values one, return exact path.
259
+ * If is multiple values field, use path without the last part
260
+ */
261
+
262
+
263
+ const getFilterValuePath = filter => {
264
+ if (filter.path.includes(".%s.") === false) {
265
+ return filter.path;
266
+ }
267
+
268
+ const paths = filter.path.split(".%s.");
269
+ return paths.shift();
270
+ };
271
+
272
+ const getFilterValuePropertyPath = filter => {
273
+ if (filter.path.includes(".%s.") === false) {
274
+ return null;
275
+ }
276
+
277
+ const paths = filter.path.split(".%s.");
278
+ return paths.pop() || null;
279
+ };
280
+
281
+ const execFilter = params => {
282
+ const {
283
+ value: plainValue,
284
+ filter
285
+ } = params;
286
+ const value = transformValue(plainValue, filter.transformValue);
287
+ const matched = filter.filterPlugin.matches({
288
+ value,
289
+ compareValue: filter.compareValue
138
290
  });
291
+
292
+ if (filter.negate) {
293
+ return matched === false;
294
+ }
295
+
296
+ return matched;
297
+ };
298
+
299
+ /**
300
+ * Unfortunately we must use the contains plugin directly as plugins do not support multi field searching.
301
+ */
302
+ const createFullTextSearch = ({
303
+ term,
304
+ fields: targetFields,
305
+ plugin
306
+ }) => {
307
+ if (!term || term.trim().length === 0 || !targetFields || targetFields.length === 0) {
308
+ return null;
309
+ }
310
+
311
+ return async ({
312
+ item,
313
+ fromStorage,
314
+ fields
315
+ }) => {
316
+ for (const targetField of targetFields) {
317
+ const field = Object.values(fields).find(field => {
318
+ return field.def.fieldId === targetField;
319
+ });
320
+
321
+ if (!field) {
322
+ throw new _error.default(`Unknown field "${targetField}" in the model.`, "UNKNOWN_FIELD", {
323
+ target: targetField
324
+ });
325
+ }
326
+
327
+ const value = await fromStorage(field.def, item.values[targetField]);
328
+
329
+ if (!value) {
330
+ continue;
331
+ }
332
+
333
+ if (plugin.matches({
334
+ value,
335
+ compareValue: term
336
+ }) === true) {
337
+ return true;
338
+ }
339
+ }
340
+
341
+ return false;
342
+ };
139
343
  };
140
344
 
141
345
  const filterItems = async params => {
142
346
  const {
143
- items,
347
+ items: records,
144
348
  where,
145
349
  plugins,
146
350
  fields,
147
- fromStorage
351
+ fromStorage,
352
+ fullTextSearch
148
353
  } = params;
149
354
  const filters = createFilters({
150
355
  plugins,
151
356
  where,
152
357
  fields
153
358
  });
154
- const results = [];
359
+ const fullTextSearchPlugin = plugins.byType(_ValueFilterPlugin.ValueFilterPlugin.type).find(plugin => plugin.getOperation() === "contains");
155
360
 
156
- for (const key in items) {
157
- if (items.hasOwnProperty(key) === false) {
158
- continue;
159
- }
160
-
161
- const item = items[key];
162
- let passed = true;
361
+ if (!fullTextSearchPlugin) {
362
+ throw new _error.default(`Missing "contains" plugin to run the full-text search.`, "MISSING_PLUGIN");
363
+ }
163
364
 
365
+ const search = createFullTextSearch(_objectSpread(_objectSpread({}, fullTextSearch), {}, {
366
+ plugin: fullTextSearchPlugin
367
+ }));
368
+ const promises = records.map(async record => {
369
+ /**
370
+ * We need to go through all the filters and apply them to the given record.
371
+ */
164
372
  for (const filter of filters) {
165
- const rawValue = _dotProp.default.get(item, filter.path);
373
+ /**
374
+ * In case is multiple values field, last part is removed from path.
375
+ * -> values.categories.id -> values.categories
376
+ */
377
+ const valuePath = getFilterValuePath(filter);
378
+
379
+ const rawValue = _dotProp.default.get(record, valuePath);
380
+
381
+ if (valuePath !== filter.path) {
382
+ /**
383
+ * Calculated is different than original because we need to search in the array of objects.
384
+ */
385
+ const propertyPath = getFilterValuePropertyPath(filter);
386
+
387
+ if (!propertyPath) {
388
+ console.log(`Cannot determine the property path of "${filter.path}".`);
389
+ continue;
390
+ }
391
+
392
+ const plainValue = await fromStorage(fields[filter.fieldId].def, rawValue);
393
+ /**
394
+ * We cannot go through the value because it is not array. Log the error and continue.
395
+ */
396
+
397
+ if (Array.isArray(plainValue) === false) {
398
+ console.log(`Cannot go through the value on ${valuePath} because it is not an array, and we expect it to be.`);
399
+ continue;
400
+ }
401
+
402
+ record = _dotProp.default.set(record, valuePath, plainValue);
403
+ const values = plainValue.map(value => {
404
+ return value[propertyPath];
405
+ });
406
+ const result = execFilter({
407
+ value: values,
408
+ filter
409
+ });
410
+
411
+ if (!result) {
412
+ return null;
413
+ }
414
+
415
+ continue;
416
+ }
166
417
 
167
418
  const plainValue = await fromStorage(fields[filter.fieldId].def, rawValue);
168
419
  /**
@@ -170,46 +421,49 @@ const filterItems = async params => {
170
421
  */
171
422
 
172
423
  if (plainValue !== rawValue) {
173
- items[key] = _dotProp.default.set(item, filter.path, plainValue);
424
+ record = _dotProp.default.set(record, filter.path, plainValue);
174
425
  }
175
426
 
176
- const value = transformValue(plainValue, filter.transformValue);
177
- const matched = filter.filterPlugin.matches({
178
- value,
179
- compareValue: filter.compareValue
427
+ const result = execFilter({
428
+ value: plainValue,
429
+ filter
180
430
  });
181
431
 
182
- if ((filter.negate ? !matched : matched) === false) {
183
- passed = false;
184
- break;
432
+ if (result === false) {
433
+ return null;
185
434
  }
186
435
  }
436
+ /**
437
+ * If we have full text search defined, run it. Otherwise just return the given record.
438
+ */
439
+
440
+
441
+ if (!search) {
442
+ return record;
443
+ }
187
444
 
188
- if (!passed) {
189
- continue;
445
+ const result = await search({
446
+ item: record,
447
+ fromStorage,
448
+ fields
449
+ });
450
+
451
+ if (!result) {
452
+ return null;
190
453
  }
191
454
 
192
- results.push(item);
193
- }
455
+ return record;
456
+ });
457
+ /**
458
+ * We run filtering as promises so it is a bit faster than in for ... of loop.
459
+ */
194
460
 
195
- return results; // return items.filter(item => {
196
- // for (const filter of filters) {
197
- // const plainValue = await fromStorage(
198
- // fields[filter.fieldId].def,
199
- // dotProp.get(item, filter.path)
200
- // );
201
- //
202
- // const value = transformValue(plainValue, filter.transformValue);
203
- // const matched = filter.filterPlugin.matches({
204
- // value,
205
- // compareValue: filter.compareValue
206
- // });
207
- // if ((filter.negate ? !matched : matched) === false) {
208
- // return false;
209
- // }
210
- // }
211
- // return true;
212
- // });
461
+ const results = await Promise.all(promises);
462
+ /**
463
+ * And filter out the null values which are returned when filter is not satisfied.
464
+ */
465
+
466
+ return results.filter(Boolean);
213
467
  };
214
468
 
215
469
  exports.filterItems = filterItems;
@@ -218,13 +472,15 @@ const extractSort = (sortBy, fields) => {
218
472
  const result = sortBy.split("_");
219
473
 
220
474
  if (result.length !== 2) {
221
- throw new _error.default("Problem in determining the sorting for the entry items.", "SORT_ERROR", {
475
+ throw new _error.default("Problem in determining the sorting for the entry items.", "SORT_EXTRACT_ERROR", {
222
476
  sortBy
223
477
  });
224
478
  }
225
479
 
226
480
  const [fieldId, order] = result;
227
- const modelField = fields[fieldId];
481
+ const modelField = Object.values(fields).find(field => {
482
+ return field.def.fieldId === fieldId;
483
+ });
228
484
 
229
485
  if (!modelField) {
230
486
  throw new _error.default("Sorting field does not exist in the content model.", "SORTING_FIELD_ERROR", {
@@ -233,8 +489,11 @@ const extractSort = (sortBy, fields) => {
233
489
  });
234
490
  }
235
491
 
236
- const valuePath = modelField.valuePath;
492
+ const valuePath = modelField.createPath({
493
+ field: modelField.def
494
+ });
237
495
  return {
496
+ field: modelField,
238
497
  fieldId,
239
498
  valuePath,
240
499
  reverse: order === "DESC"
@@ -253,7 +512,7 @@ const sortEntryItems = params => {
253
512
  } else if (sort.length === 0) {
254
513
  sort.push("savedOn_DESC");
255
514
  } else if (sort.length > 1) {
256
- throw new _error.default("Sorting is limited to a single field.", "SORT_ERROR", {
515
+ throw new _error.default("Sorting is limited to a single field.", "SORT_MULTIPLE_FIELDS_ERROR", {
257
516
  sort: sort
258
517
  });
259
518
  }
@@ -261,24 +520,24 @@ const sortEntryItems = params => {
261
520
  const [firstSort] = sort;
262
521
 
263
522
  if (!firstSort) {
264
- throw new _error.default("Empty sort array item.", "SORT_ERROR", {
523
+ throw new _error.default("Empty sort array item.", "SORT_EMPTY_ERROR", {
265
524
  sort
266
525
  });
267
526
  }
268
527
 
269
528
  const {
270
529
  fieldId,
530
+ field,
271
531
  valuePath,
272
532
  reverse
273
533
  } = extractSort(firstSort, fields);
274
- const field = fields[fieldId];
275
534
  const itemsToSort = items.map(item => {
276
535
  return {
277
536
  id: item.id,
278
537
  value: field.valueTransformer(_dotProp.default.get(item, valuePath))
279
538
  };
280
539
  });
281
- const sortedItems = (0, _lodash.default)(itemsToSort, "value");
540
+ const sortedItems = (0, _sortBy.default)(itemsToSort, "value");
282
541
  const newItems = sortedItems.map(s => {
283
542
  const item = items.find(i => i.id === s.id);
284
543
 
@@ -342,18 +601,30 @@ const buildModelFields = ({
342
601
  });
343
602
  const valuePathPlugins = getMappedPlugins({
344
603
  plugins,
345
- type: "cms-field-filter-path",
604
+ type: _CmsEntryFieldFilterPathPlugin.CmsEntryFieldFilterPathPlugin.type,
346
605
  property: "fieldType"
347
606
  });
348
607
  const fields = Object.values(_systemFields.systemFields).reduce((collection, field) => {
608
+ /**
609
+ * This should be caught on the tests runs and never actually happen on live system.
610
+ */
611
+ if (!field.fieldId) {
612
+ throw new _error.default("Missing system field `fieldId`.", "FIELD_ID_ERROR", {
613
+ field
614
+ });
615
+ }
616
+
349
617
  const transformValuePlugin = transformValuePlugins[field.type];
350
618
  const valuePathPlugin = valuePathPlugins[field.type];
351
- let valuePath;
619
+
620
+ let createPath = params => {
621
+ return params.field.fieldId;
622
+ };
352
623
 
353
624
  if (valuePathPlugin) {
354
- valuePath = valuePathPlugin.createPath({
355
- field
356
- });
625
+ createPath = params => {
626
+ return valuePathPlugin.createPath(params);
627
+ };
357
628
  }
358
629
 
359
630
  collection[field.fieldId] = {
@@ -368,23 +639,30 @@ const buildModelFields = ({
368
639
  value
369
640
  });
370
641
  },
371
- valuePath: valuePath || field.fieldId,
642
+ createPath,
372
643
  isSystemField: true
373
644
  };
374
645
  return collection;
375
646
  }, {});
376
647
  return model.fields.reduce((collection, field) => {
648
+ if (!field.fieldId) {
649
+ console.log(`Field "${field.storageId}" in model "${model.modelId}" is missing fieldId.`);
650
+ return collection;
651
+ }
652
+
377
653
  const transformValuePlugin = transformValuePlugins[field.type];
378
654
  const valuePathPlugin = valuePathPlugins[field.type];
379
- let valuePath;
655
+
656
+ let createPath = params => {
657
+ return `${VALUES_ATTRIBUTE}.${params.field.fieldId}`;
658
+ };
380
659
 
381
660
  if (valuePathPlugin) {
382
- valuePath = valuePathPlugin.createPath({
383
- field
384
- });
661
+ createPath = params => {
662
+ return valuePathPlugin.createPath(params);
663
+ };
385
664
  }
386
665
 
387
- const targetValuePath = `${VALUES_ATTRIBUTE}.${valuePath || field.fieldId}`;
388
666
  collection[field.fieldId] = {
389
667
  def: field,
390
668
  valueTransformer: value => {
@@ -397,7 +675,7 @@ const buildModelFields = ({
397
675
  value
398
676
  });
399
677
  },
400
- valuePath: targetValuePath || field.fieldId
678
+ createPath
401
679
  };
402
680
  return collection;
403
681
  }, fields);