@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.
- package/.editorconfig +23 -0
- package/package.json +33 -0
- package/src/classes/ApiFormatter.ts +338 -0
- package/src/classes/BatchStatus.ts +86 -0
- package/src/classes/ChangesCompiler.ts +161 -0
- package/src/classes/ChangesProcessor.ts +475 -0
- package/src/classes/CollectionCache.ts +234 -0
- package/src/classes/CollectionEntryService.ts +83 -0
- package/src/classes/CollectionHandler.ts +619 -0
- package/src/classes/CollectionResourceService.ts +38 -0
- package/src/classes/ColumnCache.ts +114 -0
- package/src/classes/ColumnExpansion.ts +74 -0
- package/src/classes/ColumnHandler.ts +90 -0
- package/src/classes/ColumnSet.ts +181 -0
- package/src/classes/ColumnUtil.ts +589 -0
- package/src/classes/CreateRequest.ts +158 -0
- package/src/classes/ItemAdministrator.ts +571 -0
- package/src/classes/ItemCache.ts +158 -0
- package/src/classes/ItemEntryService.ts +488 -0
- package/src/classes/ItemHandler.ts +592 -0
- package/src/classes/ItemRequestService.ts +229 -0
- package/src/classes/ItemResourceService.ts +36 -0
- package/src/classes/KeyService.ts +76 -0
- package/src/classes/LockService.ts +184 -0
- package/src/classes/ModuleChangesCompiler.ts +123 -0
- package/src/classes/PromiseService.ts +19 -0
- package/src/classes/QueryMatcher.ts +135 -0
- package/src/classes/QueryParser.ts +209 -0
- package/src/classes/QueryService.ts +35 -0
- package/src/classes/RequestColumnExpansion.ts +31 -0
- package/src/classes/ResourceCache.ts +289 -0
- package/src/classes/Util.ts +422 -0
- package/src/constants.ts +11 -0
- package/src/factories/BatchStatusFactory.ts +18 -0
- package/src/factories/ChangesCompilerFactory.ts +20 -0
- package/src/factories/ChangesProcessorFactory.ts +53 -0
- package/src/factories/CollectionCacheFactory.ts +15 -0
- package/src/factories/CollectionEntryFactory.ts +35 -0
- package/src/factories/CollectionResourceFactory.ts +36 -0
- package/src/factories/ColumnExpansionFactory.ts +25 -0
- package/src/factories/ColumnSetFactory.ts +24 -0
- package/src/factories/CreateRequestFactory.ts +28 -0
- package/src/factories/ItemCacheFactory.ts +18 -0
- package/src/factories/ItemEntryFactory.ts +18 -0
- package/src/factories/ItemResourceFactory.ts +28 -0
- package/src/factories/ModuleChangesCompilerFactory.ts +21 -0
- package/src/factories/QueryMatcherFactory.ts +20 -0
- package/src/factories/QueryParserFactory.ts +10 -0
- package/src/factories/RequestColumnExpansionFactory.ts +15 -0
- package/src/factories/ResourceCacheFactory.ts +55 -0
- package/src/index.ts +188 -0
- package/src/interfaces/ApiRequestServiceInterface.ts +17 -0
- package/src/interfaces/AuthInterface.ts +5 -0
- package/src/interfaces/ErrorHandlerInterface.ts +3 -0
- package/src/interfaces/ResourceCacheInterface.ts +9 -0
- package/src/models/CollectionEditResource.ts +139 -0
- package/src/models/CollectionEntry.ts +448 -0
- package/src/models/CollectionResource.ts +223 -0
- package/src/models/CollectionViewResource.ts +23 -0
- package/src/models/EditResource.ts +15 -0
- package/src/models/ItemEntry.ts +24 -0
- package/src/models/ItemResource.ts +35 -0
- package/src/models/ViewResource.ts +14 -0
- package/tsconfig.json +12 -0
- package/tslint.json +63 -0
- package/webpack.common.js +35 -0
- package/webpack.dev.js +10 -0
- package/webpack.prod.js +17 -0
package/.editorconfig
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs
|
|
2
|
+
# editorconfig.org
|
|
3
|
+
|
|
4
|
+
root = true
|
|
5
|
+
|
|
6
|
+
[*]
|
|
7
|
+
charset=utf-8
|
|
8
|
+
end_of_line=lf
|
|
9
|
+
indent_style = tab
|
|
10
|
+
indent_size = 4
|
|
11
|
+
|
|
12
|
+
# We recommend you to keep these unchanged
|
|
13
|
+
end_of_line = lf
|
|
14
|
+
charset = utf-8
|
|
15
|
+
trim_trailing_whitespace = true
|
|
16
|
+
insert_final_newline = true
|
|
17
|
+
|
|
18
|
+
[*.md]
|
|
19
|
+
trim_trailing_whitespace = false
|
|
20
|
+
|
|
21
|
+
[*.json]
|
|
22
|
+
indent_style = space
|
|
23
|
+
indent_size = 2
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rentman/rm-core",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Rentman core functionalities",
|
|
5
|
+
"main": "./dist/rm-core.js",
|
|
6
|
+
"typings": "./dist/definitions/index.d.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"q": "^1.5.1"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/rentmanpublic/frontend-core"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/moment": "^2.13.0",
|
|
16
|
+
"@types/q": "^1.5.0",
|
|
17
|
+
"clean-webpack-plugin": "^0.1.19",
|
|
18
|
+
"ts-loader": "^4.4.1",
|
|
19
|
+
"tslint": "^5.10.0",
|
|
20
|
+
"typescript": "^2.9.2",
|
|
21
|
+
"uglifyjs-webpack-plugin": "^1.2.5",
|
|
22
|
+
"webpack": "^4.12.0",
|
|
23
|
+
"webpack-cli": "^3.0.6",
|
|
24
|
+
"webpack-merge": "^4.1.3"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"bundle-dev": "webpack --config webpack.dev.js",
|
|
28
|
+
"bundle": "webpack --config webpack.prod.js",
|
|
29
|
+
"type-check": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"author": "Martijn Welker <martijn@rentman.nl>",
|
|
32
|
+
"license": "UNLICENSED"
|
|
33
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import * as moment from 'moment';
|
|
2
|
+
import { copy, equals } from './Util';
|
|
3
|
+
|
|
4
|
+
export default class ApiFormatter {
|
|
5
|
+
MOMENT_INVALID = 'Invalid date';
|
|
6
|
+
SERVER_DATETIME_FORMAT = 'YYYY-MM-DD[T]HH:mm:ssZ';
|
|
7
|
+
SERVER_TIME_FORMAT = 'HH:mm:ss';
|
|
8
|
+
SERVER_DATE_FORMAT = 'YYYY-MM-DD';
|
|
9
|
+
|
|
10
|
+
constructor () {
|
|
11
|
+
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
formatResponse (fields, columns) {
|
|
15
|
+
for (const column of columns) {
|
|
16
|
+
fields[column.path] = this.formatResponseValue(column.apiColumn.type, fields[column.path]);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
expandResponse (current, expanded, columns) {
|
|
21
|
+
for (const column of columns) {
|
|
22
|
+
current[column.path] = this.formatResponseValue(column.apiColumn.type, expanded[column.path]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
formatRequest (initial, modified, columns) {
|
|
27
|
+
const result = {};
|
|
28
|
+
|
|
29
|
+
for (const column of columns) {
|
|
30
|
+
const columnId = column.id;
|
|
31
|
+
const apiColumn = column.apiColumn;
|
|
32
|
+
|
|
33
|
+
if (apiColumn.readOnly)
|
|
34
|
+
continue;
|
|
35
|
+
|
|
36
|
+
if (!this.compareFields(apiColumn.type, initial[columnId], modified[columnId]))
|
|
37
|
+
result[columnId] = this.formatRequestValue(column.apiColumn.type, modified[columnId]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
hasChanges (initial, modified, columns) {
|
|
44
|
+
for (const column of columns) {
|
|
45
|
+
const columnId = column.id;
|
|
46
|
+
const apiColumn = column.apiColumn;
|
|
47
|
+
|
|
48
|
+
if (apiColumn.readOnly)
|
|
49
|
+
continue;
|
|
50
|
+
|
|
51
|
+
if (!this.compareFields(apiColumn.type, initial[columnId], modified[columnId]))
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getChanges (initial, modified, columns) {
|
|
59
|
+
const result = [];
|
|
60
|
+
|
|
61
|
+
for (const column of columns) {
|
|
62
|
+
const columnId = column.id;
|
|
63
|
+
const apiColumn = column.apiColumn;
|
|
64
|
+
|
|
65
|
+
if (apiColumn.readOnly)
|
|
66
|
+
continue;
|
|
67
|
+
|
|
68
|
+
if (!this.compareFields(apiColumn.type, initial[columnId], modified[columnId])) {
|
|
69
|
+
result.push(
|
|
70
|
+
{
|
|
71
|
+
oldValue: initial[columnId],
|
|
72
|
+
newValue: modified[columnId],
|
|
73
|
+
column: column.apiColumn
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
copyFields (source, destination, columns) {
|
|
83
|
+
for (const column of columns) {
|
|
84
|
+
const columnPath = column.path;
|
|
85
|
+
|
|
86
|
+
destination[columnPath] = this.clone(column.apiColumn.type, source[columnPath]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return destination;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
shallowCopyFields (shallowSource, deepSource, destination, columns) {
|
|
93
|
+
for (const column of columns) {
|
|
94
|
+
const columnPath = column.path;
|
|
95
|
+
|
|
96
|
+
destination[columnPath] = shallowSource[columnPath] !== undefined ?
|
|
97
|
+
shallowSource[columnPath] :
|
|
98
|
+
this.clone(column.apiColumn.type, deepSource[columnPath]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return destination;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
setDefaultValues (destination, columns) {
|
|
105
|
+
for (const column of columns) {
|
|
106
|
+
destination[column.id] = this.getDefaultValue(column.apiColumn.type);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return destination;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
compareFields (columnType, first, second) {
|
|
113
|
+
if (first === undefined)
|
|
114
|
+
return second === undefined;
|
|
115
|
+
|
|
116
|
+
if (second === undefined)
|
|
117
|
+
return false;
|
|
118
|
+
|
|
119
|
+
switch (columnType) {
|
|
120
|
+
case 'date':
|
|
121
|
+
case 'time':
|
|
122
|
+
case 'datetime':
|
|
123
|
+
return this.compareMomentObjects(first, second);
|
|
124
|
+
case 'duration':
|
|
125
|
+
return this.compareDurations(first, second);
|
|
126
|
+
case 'id': // id types
|
|
127
|
+
case 'item':
|
|
128
|
+
case 'link':
|
|
129
|
+
case 'child':
|
|
130
|
+
case 'sibling':
|
|
131
|
+
return first == second; // use non-strict comparison
|
|
132
|
+
case 'current':
|
|
133
|
+
case 'dimension':
|
|
134
|
+
case 'distance':
|
|
135
|
+
case 'double': // floating point types
|
|
136
|
+
case 'filesize':
|
|
137
|
+
case 'number':
|
|
138
|
+
case 'power':
|
|
139
|
+
case 'quantity':
|
|
140
|
+
case 'volume':
|
|
141
|
+
case 'weight':
|
|
142
|
+
case 'coordinate':
|
|
143
|
+
case 'price':
|
|
144
|
+
case 'percentage':
|
|
145
|
+
return this.compareFloats(first, second);
|
|
146
|
+
case 'int': // integer types
|
|
147
|
+
case 'bool': // boolean types
|
|
148
|
+
case 'enum':
|
|
149
|
+
case 'email': // string types
|
|
150
|
+
case 'phone':
|
|
151
|
+
case 'url':
|
|
152
|
+
case 'text':
|
|
153
|
+
case 'longtext':
|
|
154
|
+
case 'color':
|
|
155
|
+
return first === second;
|
|
156
|
+
default:
|
|
157
|
+
return equals(first, second);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
compareFloats (a, b) {
|
|
162
|
+
const absA = Math.abs(a);
|
|
163
|
+
const absB = Math.abs(b);
|
|
164
|
+
const diff = Math.abs(a - b);
|
|
165
|
+
|
|
166
|
+
const EPSILON = 1.19e-07;
|
|
167
|
+
|
|
168
|
+
// shortcut, handles infinities
|
|
169
|
+
if (a === b) {
|
|
170
|
+
return true;
|
|
171
|
+
} else if (a === 0 || b === 0 || diff < Number.MIN_VALUE) {
|
|
172
|
+
// a or b is zero or both are extremely close to it
|
|
173
|
+
// relative error is less meaningful here
|
|
174
|
+
return diff < (EPSILON * Number.MIN_VALUE);
|
|
175
|
+
} else {
|
|
176
|
+
// use relative error
|
|
177
|
+
return diff / Math.min((absA + absB), Number.MAX_VALUE) < EPSILON;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
compareMomentObjects (first, second) {
|
|
182
|
+
if (!first.isValid())
|
|
183
|
+
return !second.isValid();
|
|
184
|
+
|
|
185
|
+
if (!second.isValid())
|
|
186
|
+
return false; // first IS valid
|
|
187
|
+
|
|
188
|
+
return first.isSame(second);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
compareDurations (first, second) {
|
|
192
|
+
return first.asMilliseconds() === second.asMilliseconds();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
formatResponseValue (columnType, value) {
|
|
196
|
+
switch (columnType) {
|
|
197
|
+
case 'datetime':
|
|
198
|
+
case 'date':
|
|
199
|
+
return this.formatResponseDate(value);
|
|
200
|
+
case 'color':
|
|
201
|
+
return this.formatResponseColor(value);
|
|
202
|
+
case 'duration':
|
|
203
|
+
return this.formatResponseDuration(value);
|
|
204
|
+
case 'json':
|
|
205
|
+
return typeof value === 'string' ? JSON.parse(value) : value;
|
|
206
|
+
case 'number':
|
|
207
|
+
return value === 0 ? null : value;
|
|
208
|
+
case 'filesize':
|
|
209
|
+
return value / 1000000;
|
|
210
|
+
default:
|
|
211
|
+
return value;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
formatRequestValue (columnType, value) {
|
|
216
|
+
switch (columnType) {
|
|
217
|
+
case 'datetime':
|
|
218
|
+
return this.formatRequestDateTime(value);
|
|
219
|
+
case 'date':
|
|
220
|
+
return this.formatRequestDate(value);
|
|
221
|
+
case 'color':
|
|
222
|
+
return this.formatRequestColor(value);
|
|
223
|
+
case 'duration':
|
|
224
|
+
return this.formatRequestDuration(value);
|
|
225
|
+
case 'number':
|
|
226
|
+
return value === 0 ? null : value;
|
|
227
|
+
default:
|
|
228
|
+
return value;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
formatResponseDate (value) {
|
|
233
|
+
return value === null ? moment.invalid() : moment(value);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
formatResponseDuration (value) {
|
|
237
|
+
return moment.duration(value === null ? 0 : value, 'seconds');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
formatResponseColor (value) {
|
|
241
|
+
return value.charAt(0) === '#' ? value : '#' + value;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
formatRequestDateTime (value) {
|
|
245
|
+
return value.isValid() ? value.format(this.SERVER_DATETIME_FORMAT) : null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
formatRequestDate (value) {
|
|
249
|
+
return value.isValid() ? value.format(this.SERVER_DATE_FORMAT) : null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
formatRequestDuration (value) {
|
|
253
|
+
return value.asSeconds();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
formatRequestColor (value) {
|
|
257
|
+
return value.charAt(0) === '#' ? value.substring(1) : value;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
getDefaultValue (columnType) {
|
|
261
|
+
switch (columnType) {
|
|
262
|
+
case 'int':
|
|
263
|
+
case 'double':
|
|
264
|
+
case 'percentage':
|
|
265
|
+
case 'price':
|
|
266
|
+
return 0;
|
|
267
|
+
case 'item':
|
|
268
|
+
case 'link':
|
|
269
|
+
case 'child':
|
|
270
|
+
case 'sibling':
|
|
271
|
+
return null;
|
|
272
|
+
case 'bool':
|
|
273
|
+
return false;
|
|
274
|
+
case 'enum':
|
|
275
|
+
return '0';
|
|
276
|
+
case 'date':
|
|
277
|
+
case 'datetime':
|
|
278
|
+
case 'time':
|
|
279
|
+
return moment.invalid();
|
|
280
|
+
case 'duration':
|
|
281
|
+
return moment.duration();
|
|
282
|
+
case 'text':
|
|
283
|
+
case 'longtext':
|
|
284
|
+
return '';
|
|
285
|
+
case 'color':
|
|
286
|
+
return '#000000';
|
|
287
|
+
default:
|
|
288
|
+
return '';
|
|
289
|
+
// email
|
|
290
|
+
// url
|
|
291
|
+
// html
|
|
292
|
+
// json
|
|
293
|
+
// data
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
isDefaultValue (columnType, value) {
|
|
298
|
+
return this.compareFields(columnType, value, this.getDefaultValue(columnType));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
clone (columnType, value) {
|
|
302
|
+
switch (columnType) {
|
|
303
|
+
case 'duration':
|
|
304
|
+
return this.cloneDuration(value);
|
|
305
|
+
case 'date':
|
|
306
|
+
case 'datetime':
|
|
307
|
+
return moment(value);
|
|
308
|
+
case 'json':
|
|
309
|
+
return copy(value);
|
|
310
|
+
default:
|
|
311
|
+
return value;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
getCloneFunction (columnType) {
|
|
316
|
+
switch (columnType) {
|
|
317
|
+
case 'duration':
|
|
318
|
+
return this.cloneDuration;
|
|
319
|
+
case 'date':
|
|
320
|
+
case 'datetime':
|
|
321
|
+
return this.cloneDate;
|
|
322
|
+
default:
|
|
323
|
+
return this.identity;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
cloneDuration (duration) {
|
|
328
|
+
return moment.duration(duration);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
cloneDate (date) {
|
|
332
|
+
return date.clone();
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
identity (input) {
|
|
336
|
+
return input;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ENTRY_STATUS } from "../constants";
|
|
2
|
+
import ItemEntry from "../models/ItemEntry";
|
|
3
|
+
import PromiseService from "./PromiseService";
|
|
4
|
+
|
|
5
|
+
export default class BatchStatus {
|
|
6
|
+
entries = {};
|
|
7
|
+
|
|
8
|
+
constructor (
|
|
9
|
+
private promiseService: PromiseService,
|
|
10
|
+
public getItemId: (itemEntry: ItemEntry) => number,
|
|
11
|
+
public getPromise: (itemEntry: ItemEntry) => Promise<any> // todo: promise return type
|
|
12
|
+
) {
|
|
13
|
+
for (const status in ENTRY_STATUS)
|
|
14
|
+
this.entries[ENTRY_STATUS[status]] = [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
add (entryStatus) {
|
|
18
|
+
const status = entryStatus.status;
|
|
19
|
+
|
|
20
|
+
if (this.entries[status] === undefined)
|
|
21
|
+
this.entries[status] = [];
|
|
22
|
+
|
|
23
|
+
this.entries[status].push(entryStatus.entry);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
hasEntries (statusArray) {
|
|
27
|
+
for (const status of statusArray) {
|
|
28
|
+
if (this.entries[status].length > 0)
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
hasRequests () {
|
|
36
|
+
return this.hasEntries([ENTRY_STATUS.RESOLVE, ENTRY_STATUS.EXPAND, ENTRY_STATUS.PROMISED_EXPAND]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
forEachEntry (statusArray: any[], callback) {
|
|
40
|
+
for (const status of statusArray) {
|
|
41
|
+
const statusEntries = this.entries[status];
|
|
42
|
+
|
|
43
|
+
for (const statusEntry of statusEntries)
|
|
44
|
+
callback(statusEntry);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getPromises () {
|
|
49
|
+
const promises = [];
|
|
50
|
+
|
|
51
|
+
this.forEachEntry(
|
|
52
|
+
[ENTRY_STATUS.PROMISED, ENTRY_STATUS.PROMISED_EXPAND],
|
|
53
|
+
entry => {
|
|
54
|
+
promises.push(this.getPromise(entry));
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return this.promiseService.all(promises);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getRequestIds (): number[] {
|
|
62
|
+
const result = [];
|
|
63
|
+
|
|
64
|
+
this.forEachEntry(
|
|
65
|
+
[ENTRY_STATUS.RESOLVE, ENTRY_STATUS.EXPAND, ENTRY_STATUS.PROMISED_EXPAND],
|
|
66
|
+
entry => {
|
|
67
|
+
result.push(this.getItemId(entry));
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
getArray () {
|
|
75
|
+
const result = [];
|
|
76
|
+
|
|
77
|
+
for (const status in this.entries) {
|
|
78
|
+
const statusEntries = this.entries[status];
|
|
79
|
+
|
|
80
|
+
for (const statusEntry of statusEntries)
|
|
81
|
+
result.push(statusEntry);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import CollectionEntry from "../models/CollectionEntry";
|
|
2
|
+
import ItemEntry from "../models/ItemEntry";
|
|
3
|
+
import ItemEntryService from "./ItemEntryService";
|
|
4
|
+
import { createPath } from "./Util";
|
|
5
|
+
|
|
6
|
+
export default class ChangesCompiler {
|
|
7
|
+
compilation = {
|
|
8
|
+
create: {},
|
|
9
|
+
delete: {},
|
|
10
|
+
update: {},
|
|
11
|
+
move: {}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
private moveArray: ItemEntry[] = [];
|
|
15
|
+
private deleteMap = {};
|
|
16
|
+
|
|
17
|
+
constructor (
|
|
18
|
+
private itemEntryService: ItemEntryService,
|
|
19
|
+
public singleItems: ItemEntry[],
|
|
20
|
+
public collections: CollectionEntry[],
|
|
21
|
+
private collectionOptions
|
|
22
|
+
) {
|
|
23
|
+
this.processSingleItems();
|
|
24
|
+
this.processCollections();
|
|
25
|
+
this.postProcess();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getParent (itemEntry: ItemEntry) {
|
|
29
|
+
return itemEntry.parent !== undefined ?
|
|
30
|
+
itemEntry.parent :
|
|
31
|
+
{itemType: 'Void', itemId: 0};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
addMoveEntry (itemEntry) {
|
|
35
|
+
this.moveArray.push(itemEntry);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
addDeleteEntry (itemEntry, parentId, parentType) {
|
|
39
|
+
createPath(
|
|
40
|
+
this.deleteMap,
|
|
41
|
+
[itemEntry.itemType, itemEntry.itemId],
|
|
42
|
+
[{}, {parentId: parentId, item: itemEntry, parentType: parentType}]
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
addUpdateEntry (itemEntry) {
|
|
47
|
+
createPath(
|
|
48
|
+
this.compilation.update,
|
|
49
|
+
[itemEntry.itemType, itemEntry.itemId],
|
|
50
|
+
[{}, itemEntry]
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
addCreateEntry (itemEntry) {
|
|
55
|
+
const parent = this.getParent(itemEntry);
|
|
56
|
+
|
|
57
|
+
createPath(
|
|
58
|
+
this.compilation.create,
|
|
59
|
+
[parent.itemType, parent.itemId, itemEntry.itemType, itemEntry.itemId],
|
|
60
|
+
[{}, {}, {}, itemEntry]
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
postProcess () {
|
|
65
|
+
for (const itemEntry of this.moveArray) {
|
|
66
|
+
const itemId = itemEntry.itemId;
|
|
67
|
+
const deleteEntry = this.deleteMap[itemEntry.itemType];
|
|
68
|
+
|
|
69
|
+
if (deleteEntry === undefined || deleteEntry[itemId] === undefined)
|
|
70
|
+
continue;
|
|
71
|
+
|
|
72
|
+
// assume the itemEntry has a parent (or something went seriously wrong)
|
|
73
|
+
const parent = itemEntry.parent;
|
|
74
|
+
const destination = createPath(
|
|
75
|
+
this.compilation.move,
|
|
76
|
+
[parent.itemType, parent.itemId],
|
|
77
|
+
[{}, []]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
destination.push(deleteEntry[itemId]);
|
|
81
|
+
|
|
82
|
+
delete deleteEntry[itemId];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (const itemType in this.deleteMap) {
|
|
86
|
+
for (const itemId in this.deleteMap[itemType]) {
|
|
87
|
+
const itemEntry: ItemEntry = this.deleteMap[itemType][itemId].item;
|
|
88
|
+
const destination = this.compilation.delete;
|
|
89
|
+
|
|
90
|
+
if (destination[itemType] === undefined)
|
|
91
|
+
destination[itemType] = [];
|
|
92
|
+
|
|
93
|
+
destination[itemType].push(itemEntry);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private processSingleItems () {
|
|
99
|
+
for (const itemEntry of this.singleItems) {
|
|
100
|
+
if (this.itemEntryService.isLocal(itemEntry))
|
|
101
|
+
this.addCreateEntry(itemEntry);
|
|
102
|
+
else if (this.itemEntryService.hasChanges(itemEntry))
|
|
103
|
+
this.addUpdateEntry(itemEntry);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private isEditableCollection (collectionEntry) {
|
|
108
|
+
if (this.collectionOptions === undefined)
|
|
109
|
+
return true;
|
|
110
|
+
|
|
111
|
+
return this.collectionOptions[collectionEntry.id].edit;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private hasEditableItems (collectionEntry) {
|
|
115
|
+
if (this.collectionOptions === undefined)
|
|
116
|
+
return true;
|
|
117
|
+
|
|
118
|
+
return this.collectionOptions[collectionEntry.id].editItems;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private processCollections () {
|
|
122
|
+
for (const collectionEntry of this.collections) {
|
|
123
|
+
if (collectionEntry.isDeferred())
|
|
124
|
+
continue;
|
|
125
|
+
|
|
126
|
+
// add the collection parent id to the delete entry (in case the item has been moved)
|
|
127
|
+
const parent = collectionEntry.parent;
|
|
128
|
+
const parentId = parent !== null ? parent.itemId : 0;
|
|
129
|
+
|
|
130
|
+
if (this.isEditableCollection(collectionEntry)) {
|
|
131
|
+
let addedItems;
|
|
132
|
+
let removedItems;
|
|
133
|
+
|
|
134
|
+
if (collectionEntry.isLocal()) {
|
|
135
|
+
addedItems = collectionEntry.items;
|
|
136
|
+
removedItems = [];
|
|
137
|
+
} else {
|
|
138
|
+
addedItems = collectionEntry.getAddedItems();
|
|
139
|
+
removedItems = collectionEntry.getRemovedItems();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const addedItem of addedItems) {
|
|
143
|
+
if (this.itemEntryService.isLocal(addedItem))
|
|
144
|
+
this.addCreateEntry(addedItem);
|
|
145
|
+
else
|
|
146
|
+
this.addMoveEntry(addedItem);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
for (const removedItem of removedItems)
|
|
150
|
+
this.addDeleteEntry(removedItem, parentId, parent.itemType);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (this.hasEditableItems(collectionEntry)) {
|
|
154
|
+
const updatedItems = collectionEntry.getUpdatedItems();
|
|
155
|
+
|
|
156
|
+
for (const updatedItem of updatedItems)
|
|
157
|
+
this.addUpdateEntry(updatedItem);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|