@rentman/rm-core 1.0.0

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 (68) hide show
  1. package/.editorconfig +23 -0
  2. package/package.json +33 -0
  3. package/src/classes/ApiFormatter.ts +338 -0
  4. package/src/classes/BatchStatus.ts +86 -0
  5. package/src/classes/ChangesCompiler.ts +161 -0
  6. package/src/classes/ChangesProcessor.ts +475 -0
  7. package/src/classes/CollectionCache.ts +234 -0
  8. package/src/classes/CollectionEntryService.ts +83 -0
  9. package/src/classes/CollectionHandler.ts +619 -0
  10. package/src/classes/CollectionResourceService.ts +38 -0
  11. package/src/classes/ColumnCache.ts +114 -0
  12. package/src/classes/ColumnExpansion.ts +74 -0
  13. package/src/classes/ColumnHandler.ts +90 -0
  14. package/src/classes/ColumnSet.ts +181 -0
  15. package/src/classes/ColumnUtil.ts +589 -0
  16. package/src/classes/CreateRequest.ts +158 -0
  17. package/src/classes/ItemAdministrator.ts +571 -0
  18. package/src/classes/ItemCache.ts +158 -0
  19. package/src/classes/ItemEntryService.ts +488 -0
  20. package/src/classes/ItemHandler.ts +592 -0
  21. package/src/classes/ItemRequestService.ts +229 -0
  22. package/src/classes/ItemResourceService.ts +36 -0
  23. package/src/classes/KeyService.ts +76 -0
  24. package/src/classes/LockService.ts +184 -0
  25. package/src/classes/ModuleChangesCompiler.ts +123 -0
  26. package/src/classes/PromiseService.ts +19 -0
  27. package/src/classes/QueryMatcher.ts +135 -0
  28. package/src/classes/QueryParser.ts +209 -0
  29. package/src/classes/QueryService.ts +35 -0
  30. package/src/classes/RequestColumnExpansion.ts +31 -0
  31. package/src/classes/ResourceCache.ts +289 -0
  32. package/src/classes/Util.ts +422 -0
  33. package/src/constants.ts +11 -0
  34. package/src/factories/BatchStatusFactory.ts +18 -0
  35. package/src/factories/ChangesCompilerFactory.ts +20 -0
  36. package/src/factories/ChangesProcessorFactory.ts +53 -0
  37. package/src/factories/CollectionCacheFactory.ts +15 -0
  38. package/src/factories/CollectionEntryFactory.ts +35 -0
  39. package/src/factories/CollectionResourceFactory.ts +36 -0
  40. package/src/factories/ColumnExpansionFactory.ts +25 -0
  41. package/src/factories/ColumnSetFactory.ts +24 -0
  42. package/src/factories/CreateRequestFactory.ts +28 -0
  43. package/src/factories/ItemCacheFactory.ts +18 -0
  44. package/src/factories/ItemEntryFactory.ts +18 -0
  45. package/src/factories/ItemResourceFactory.ts +28 -0
  46. package/src/factories/ModuleChangesCompilerFactory.ts +21 -0
  47. package/src/factories/QueryMatcherFactory.ts +20 -0
  48. package/src/factories/QueryParserFactory.ts +10 -0
  49. package/src/factories/RequestColumnExpansionFactory.ts +15 -0
  50. package/src/factories/ResourceCacheFactory.ts +55 -0
  51. package/src/index.ts +188 -0
  52. package/src/interfaces/ApiRequestServiceInterface.ts +17 -0
  53. package/src/interfaces/AuthInterface.ts +5 -0
  54. package/src/interfaces/ErrorHandlerInterface.ts +3 -0
  55. package/src/interfaces/ResourceCacheInterface.ts +9 -0
  56. package/src/models/CollectionEditResource.ts +139 -0
  57. package/src/models/CollectionEntry.ts +448 -0
  58. package/src/models/CollectionResource.ts +223 -0
  59. package/src/models/CollectionViewResource.ts +23 -0
  60. package/src/models/EditResource.ts +15 -0
  61. package/src/models/ItemEntry.ts +24 -0
  62. package/src/models/ItemResource.ts +35 -0
  63. package/src/models/ViewResource.ts +14 -0
  64. package/tsconfig.json +12 -0
  65. package/tslint.json +63 -0
  66. package/webpack.common.js +35 -0
  67. package/webpack.dev.js +10 -0
  68. package/webpack.prod.js +17 -0
@@ -0,0 +1,589 @@
1
+ import * as moment from 'moment';
2
+ import { COLUMN_SEPERATOR, RENDER_AS_SEPERATOR } from '../constants';
3
+
4
+ import { copy, extend, getFirstProperty, isDefined } from './Util';
5
+
6
+ export default class ColumnUtil {
7
+ constructor () {
8
+ }
9
+
10
+ getColumnPath (columns, renderAs) {
11
+ let seperator = '';
12
+ let result = '';
13
+
14
+ for (const column of columns) {
15
+ result += seperator + column.id;
16
+ seperator = COLUMN_SEPERATOR;
17
+ }
18
+
19
+ if (renderAs !== null)
20
+ result += RENDER_AS_SEPERATOR + renderAs;
21
+
22
+ return result;
23
+ }
24
+
25
+ getDateFormatOptions (columnType) {
26
+ const now = moment();
27
+
28
+ const formats = {
29
+ 'J-M-Y': now.format('DD-MM-YYYY'),
30
+ 'J-M': now.format('DD-MM'),
31
+ 'j+F+Y': now.format('D MMM YYYY'),
32
+ 'D+j+F+Y': now.format('dddd D MMM YYYY'),
33
+ 'F': now.format('MMM'),
34
+ 'f': now.format('MMMM'),
35
+ 'D': now.format('dddd'),
36
+ 'd': now.format('ddd')
37
+ };
38
+
39
+ if (columnType === 'datetime')
40
+ extend(formats, {
41
+ 'J-M-Y+H:i': now.format('DD-MM-YYYY HH:mm'),
42
+ 'J-M+H:i': now.format('DD-MM HH:mm'),
43
+ 'j+F+Y+H:i': now.format('DD MMMM Y HH:mm'),
44
+ 'D+j+F+Y+H:i': now.format('dddd DD MMM Y HH:mm'),
45
+ 'H:i': now.format('HH:mm'),
46
+ });
47
+
48
+ return formats;
49
+ }
50
+
51
+ isFloatColumn (column) {
52
+ switch (column.type) {
53
+ case 'double': // floating point types
54
+ case 'volume':
55
+ case 'weight':
56
+ case 'distance':
57
+ case 'dimension':
58
+ case 'power':
59
+ case 'current':
60
+ case 'price':
61
+ case 'percentage':
62
+ return true;
63
+ default:
64
+ return false;
65
+ }
66
+ }
67
+
68
+ isIntColumn (column) {
69
+ switch (column.type) {
70
+ case 'int': // integer types
71
+ case 'number':
72
+ case 'quantity':
73
+ return true;
74
+ default:
75
+ return false;
76
+ }
77
+ }
78
+
79
+ isNumberColumn (column) {
80
+ return this.isFloatColumn(column) || this.isIntColumn(column);
81
+ }
82
+
83
+ isCustomColumn (column) {
84
+ return column.fieldtype === 'customfield';
85
+ }
86
+
87
+ isItemColumn (column, includeId = true) {
88
+ switch (column.type) {
89
+ case 'id':
90
+ return includeId;
91
+ case 'item':
92
+ case 'child':
93
+ case 'sibling':
94
+ case 'link':
95
+ return true;
96
+ default:
97
+ return false;
98
+ }
99
+ }
100
+
101
+ isDateColumn (column) {
102
+ switch (column.type) {
103
+ case 'datetime':
104
+ case 'date':
105
+ return true;
106
+ default:
107
+ return false;
108
+ }
109
+ }
110
+
111
+ isRenderedColumn (column) {
112
+ switch (column.type) {
113
+ case 'datetime':
114
+ case 'date':
115
+ case 'duration':
116
+ return true;
117
+ default:
118
+ return false;
119
+ }
120
+ }
121
+
122
+ isFilteredColumn (column) {
123
+ // ugly hack :-(
124
+ if (column.id === 'staffel')
125
+ return true;
126
+
127
+ if (column.generator !== undefined)
128
+ return true;
129
+
130
+ switch (column.type) {
131
+ case 'price':
132
+ case 'bool':
133
+ case 'enum':
134
+ case 'volume':
135
+ case 'weight':
136
+ case 'distance':
137
+ case 'dimension':
138
+ case 'quantity':
139
+ case 'email':
140
+ case 'url':
141
+ case 'datetime':
142
+ case 'date':
143
+ case 'time':
144
+ case 'duration':
145
+ case 'html':
146
+ case 'textreplace':
147
+ case 'longtext':
148
+ case 'percentage':
149
+ return true;
150
+ default:
151
+ return false;
152
+ }
153
+ }
154
+
155
+ isFormattedColumn (column) {
156
+ switch (column.type) {
157
+ case 'datetime':
158
+ case 'date':
159
+ case 'time':
160
+ case 'duration':
161
+ case 'color':
162
+ case 'json':
163
+ case 'number':
164
+ case 'filesize':
165
+ return true;
166
+ default:
167
+ return false;
168
+ }
169
+ }
170
+
171
+ isSelectableColumn (column) {
172
+ switch (column.type) {
173
+ case 'blob':
174
+ case 'coordinate':
175
+ case 'forceupdate':
176
+ case 'id':
177
+ case 'json':
178
+ case 'order':
179
+ case 'parentid':
180
+ case 'parenttype':
181
+ case 'password':
182
+ case 'token':
183
+ return false;
184
+ }
185
+
186
+ return true;
187
+ }
188
+
189
+ hasLocationProperty (column, location) {
190
+ // tslint:disable:no-bitwise
191
+ return (location & column.location) !== 0;
192
+ }
193
+
194
+ isEditableColumn (column) {
195
+ if (column.readOnly)
196
+ return false;
197
+
198
+ switch (column.type) {
199
+ case 'date':
200
+ case 'datetime':
201
+ case 'duration':
202
+ return false;
203
+ }
204
+
205
+ return true;
206
+ }
207
+
208
+ isSearchableColumn (column) {
209
+ switch (column.type) {
210
+ case 'email':
211
+ case 'phone':
212
+ case 'text':
213
+ case 'longtext':
214
+ case 'html':
215
+ return true;
216
+ default:
217
+ return false;
218
+ }
219
+ }
220
+
221
+ getDefaultWeight (column) {
222
+ // console.log('getDefaultWeight', column);
223
+
224
+ switch (column.type) {
225
+ case 'double':
226
+ case 'price':
227
+ case 'bool':
228
+ case 'int':
229
+ case 'percentage':
230
+ case 'color':
231
+ case 'number':
232
+ case 'power':
233
+ case 'current':
234
+ return 0.5;
235
+
236
+ case 'url':
237
+ case 'enum':
238
+ case 'email':
239
+ case 'text':
240
+ case 'item':
241
+ case 'link':
242
+ return 1.5;
243
+
244
+ case 'longtext':
245
+ case 'html':
246
+ case 'textreplace':
247
+ return 2;
248
+
249
+ case 'date':
250
+ case 'datetime':
251
+ case 'time':
252
+ case 'duration':
253
+ return 1;
254
+ default:
255
+ return 1;
256
+ }
257
+ }
258
+
259
+ getComparators (column) {
260
+ switch (column.type) {
261
+ case 'dimension':
262
+ case 'weight':
263
+ case 'price':
264
+ return ['=', '>', '<'];
265
+ case 'date':
266
+ case 'datetime':
267
+ case 'time':
268
+ case 'duration':
269
+ return ['>', '<'];
270
+ default:
271
+ return ['=', '!='];
272
+ }
273
+ }
274
+
275
+ // returns whether the first column set is a subset of the second
276
+ isColumnSubset (first, second, secondTrue) {
277
+ if (first === true) // === operator needed here
278
+ return secondTrue;
279
+
280
+ // when secondTrue is the case we need to check for derived columns
281
+ for (const column of first) {
282
+ if (!this.containsColumn(column, second))
283
+ return false;
284
+ }
285
+
286
+ return true;
287
+ }
288
+
289
+ containsColumn (requestColumn, destination) {
290
+ const column = this.parseRequestColumn(requestColumn);
291
+ const columnIndex = this.findColumn(column.id, destination);
292
+
293
+ if (columnIndex === -1)
294
+ return false;
295
+
296
+ if (column.children.length === 0)
297
+ return true;
298
+
299
+ if (typeof destination[columnIndex] === 'string')
300
+ return false;
301
+
302
+ for (const childColumn of column.children) {
303
+ if (
304
+ !this.containsColumn(
305
+ childColumn,
306
+ destination[columnIndex][column.id]
307
+ )
308
+ )
309
+ return false;
310
+ }
311
+
312
+ return true;
313
+ }
314
+
315
+ responseColumnsAvailable (itemType, requestColumns, responseColumnMap) {
316
+ for (const requestColumn of requestColumns) {
317
+ const parsedColumn = this.parseRequestColumn(requestColumn);
318
+ const children = parsedColumn.children;
319
+ const responseColumns = responseColumnMap[itemType];
320
+ let responseColumn;
321
+
322
+ if (responseColumns === undefined)
323
+ return false;
324
+
325
+ responseColumn = responseColumns[parsedColumn.id];
326
+
327
+ if (responseColumn === undefined)
328
+ return false;
329
+
330
+ if (children.length > 0 && !this.responseColumnsAvailable(responseColumn.itemType, children, responseColumnMap))
331
+ return false;
332
+ }
333
+
334
+ return true;
335
+ }
336
+
337
+ /**
338
+ * Difference: columns that are in second, but not in first
339
+ * @param {Array} first
340
+ * @param {Array} second
341
+ * @returns {Object} An object containing 'merged' and 'difference' properties
342
+ */
343
+ mergeRequestColumns (first, second) {
344
+ if (first === true) // === operator needed here
345
+ return {merged: true, difference: []};
346
+
347
+ if (second === true)
348
+ return {merged: true, difference: true};
349
+
350
+ const merged = copy(first);
351
+ const difference = [];
352
+
353
+ for (const column of second) {
354
+ const addedColumn = this.mergeRequestColumn(column, merged);
355
+
356
+ if (addedColumn !== null)
357
+ difference.push(addedColumn);
358
+ }
359
+
360
+ return {merged: merged, difference: difference};
361
+ }
362
+
363
+ mergeRequestColumn (requestColumn, destination) {
364
+ const column = this.parseRequestColumn(requestColumn);
365
+ const columnIndex = this.findColumn(column.id, destination);
366
+
367
+ if (columnIndex === -1) {
368
+ destination.push(requestColumn);
369
+ return requestColumn;
370
+ }
371
+
372
+ if (column.children.length === 0)
373
+ return null;
374
+
375
+ if (typeof destination[columnIndex] === 'string') {
376
+ destination[columnIndex] = {
377
+ [column.id]: []
378
+ };
379
+ }
380
+
381
+ const addedColumns = [];
382
+
383
+ for (const childColumn of column.children) {
384
+ const addedColumn = this.mergeRequestColumn(
385
+ childColumn,
386
+ destination[columnIndex][column.id]
387
+ );
388
+
389
+ if (addedColumn !== null)
390
+ addedColumns.push(addedColumn);
391
+ }
392
+
393
+ if (addedColumns.length === 0)
394
+ return null;
395
+
396
+ return {
397
+ [column.id]: addedColumns
398
+ };
399
+ }
400
+
401
+ parseRequestColumn (requestColumn) {
402
+ const isLeaf = typeof requestColumn === 'string';
403
+ const columnId = isLeaf ? requestColumn : getFirstProperty(requestColumn);
404
+
405
+ return {
406
+ id: columnId,
407
+ children: isLeaf ? [] : requestColumn[columnId]
408
+ };
409
+ }
410
+
411
+ /**
412
+ * Merges two sets of response columns.
413
+ * @param {object} first
414
+ * @param {object} second
415
+ * @returns {object} An object containing a 'merged' and a 'difference' property.
416
+ */
417
+ mergeResponseColumns (first, second) {
418
+ const merged = {};
419
+ const difference = {};
420
+
421
+ for (const itemType in first) {
422
+ if (second[itemType] === undefined) {
423
+ merged[itemType] = first[itemType];
424
+
425
+ continue;
426
+ }
427
+
428
+ const mergeResult = this.mergeResponseColumnMap(
429
+ first[itemType],
430
+ second[itemType]
431
+ );
432
+
433
+ merged[itemType] = mergeResult.merged;
434
+ difference[itemType] = mergeResult.difference;
435
+ }
436
+
437
+ for (const itemType in second) {
438
+ if (first[itemType] === undefined) {
439
+ merged[itemType] = second[itemType];
440
+ difference[itemType] = second[itemType];
441
+ }
442
+ }
443
+
444
+ return {merged: merged, difference: difference};
445
+ }
446
+
447
+ /**
448
+ * @param {array} first
449
+ * @param {array} second
450
+ * @returns {object} difference: the columns that are in second, but not in first.
451
+ */
452
+ mergeResponseColumnMap (first, second) {
453
+ const merged = {};
454
+ const difference = {};
455
+
456
+ for (const columnId in first)
457
+ merged[columnId] = first[columnId];
458
+
459
+ for (const columnId in second) {
460
+ if (isDefined(first[columnId]))
461
+ continue;
462
+
463
+ merged[columnId] = second[columnId];
464
+ difference[columnId] = second[columnId];
465
+ }
466
+
467
+ return {merged: merged, difference: difference};
468
+ }
469
+
470
+ isLocalColumnId (columnId) {
471
+ return columnId.substring(0, 2) === '__';
472
+ }
473
+
474
+ processConfigColumns (columns) {
475
+ const request = [];
476
+
477
+ for (const column of columns) {
478
+ if (this.isLocalColumnId(column))
479
+ continue;
480
+
481
+ this.processConfigColumn(this.parseConfigColumn(column), request);
482
+ }
483
+
484
+ return request;
485
+ }
486
+
487
+ processLocalConfigColumns (configColumns, localColumns, remoteColumnCallback, localColumnCallback) {
488
+ const localColumnMap = {};
489
+
490
+ for (const localColumn of localColumns)
491
+ localColumnMap[localColumn.id] = localColumn;
492
+
493
+ for (const configId of configColumns) {
494
+ const parsedConfigId = configId.split(RENDER_AS_SEPERATOR);
495
+ const columnId = parsedConfigId[0];
496
+
497
+ if (!this.isLocalColumnId(columnId)) {
498
+ remoteColumnCallback(configId, columnId, parsedConfigId[1]);
499
+
500
+ continue;
501
+ }
502
+
503
+ // column might not be available in localColumns
504
+ if (localColumnMap[columnId] === undefined)
505
+ continue;
506
+
507
+ localColumnCallback(configId, localColumnMap[columnId]);
508
+ delete localColumnMap[columnId];
509
+ }
510
+ }
511
+
512
+ consolidateColumns (configColumns, localColumns, addRequestColumns) {
513
+ const filteredLocalColumns = [];
514
+ let requestColumns = copy(addRequestColumns);
515
+
516
+ this.processLocalConfigColumns(
517
+ configColumns,
518
+ localColumns,
519
+ (configId, columnId) => {
520
+ this.processConfigColumn(columnId.split(COLUMN_SEPERATOR), requestColumns);
521
+ },
522
+ (configId, localColumn) => {
523
+ filteredLocalColumns.push(localColumn);
524
+ requestColumns = this.mergeRequestColumns(
525
+ requestColumns,
526
+ localColumn.valueOptions.requestColumns
527
+ ).merged;
528
+ }
529
+ );
530
+
531
+ return {request: requestColumns, local: filteredLocalColumns};
532
+ }
533
+
534
+ parseConfigColumn (column) {
535
+ return column.split(RENDER_AS_SEPERATOR)[0].split(COLUMN_SEPERATOR);
536
+ }
537
+
538
+ processConfigColumn (columnArray, destination) {
539
+ const columnId = columnArray[0];
540
+ const isLeaf = columnArray.length === 1;
541
+
542
+ if (isLeaf) {
543
+ if (destination.indexOf(columnId) === -1)
544
+ destination.push(columnId);
545
+
546
+ return;
547
+ }
548
+
549
+ let columnIndex = this.findColumnObject(columnId, destination);
550
+
551
+ if (columnIndex === -1) {
552
+ columnIndex = destination.push({
553
+ [columnId]: []
554
+ }) - 1;
555
+ }
556
+
557
+ this.processConfigColumn(
558
+ columnArray.slice(1),
559
+ destination[columnIndex][columnId]
560
+ );
561
+ }
562
+
563
+ findColumnObject (columnId, array) {
564
+ for (let i = 0; i < array.length; i++) {
565
+ if (getFirstProperty(array[i]) === columnId)
566
+ return i;
567
+ }
568
+
569
+ return -1;
570
+ }
571
+
572
+ findColumn (columnId, array) {
573
+ for (let i = 0; i < array.length; i++) {
574
+ const column = array[i];
575
+
576
+ if (typeof column === 'string') {
577
+ if (column === columnId)
578
+ return i;
579
+
580
+ continue;
581
+ }
582
+
583
+ if (getFirstProperty(column) === columnId)
584
+ return i;
585
+ }
586
+
587
+ return -1;
588
+ }
589
+ }