@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
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import * as moment from 'moment';
|
|
2
|
+
import ApiFormatter from './ApiFormatter';
|
|
3
|
+
import { hasProperties, inArray } from './Util';
|
|
4
|
+
|
|
5
|
+
const MAYBE = 'maybe';
|
|
6
|
+
|
|
7
|
+
export default class QueryMatcher {
|
|
8
|
+
constructor (
|
|
9
|
+
private apiFormatter: ApiFormatter,
|
|
10
|
+
public parentType: string,
|
|
11
|
+
public parentId: number,
|
|
12
|
+
public fields: object,
|
|
13
|
+
public itemType?: string
|
|
14
|
+
) {
|
|
15
|
+
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private compareValues (value, queryValue) {
|
|
19
|
+
if (moment.isMoment(queryValue))
|
|
20
|
+
return this.apiFormatter.compareMomentObjects(queryValue, value);
|
|
21
|
+
|
|
22
|
+
if (moment.isDuration(queryValue))
|
|
23
|
+
return this.apiFormatter.compareDurations(queryValue, value);
|
|
24
|
+
|
|
25
|
+
return queryValue == value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
matchQuery (query: any): boolean | string {
|
|
29
|
+
if (query.search !== undefined)
|
|
30
|
+
return MAYBE;
|
|
31
|
+
|
|
32
|
+
if (query.custom !== undefined)
|
|
33
|
+
return MAYBE;
|
|
34
|
+
|
|
35
|
+
if (query.linkedTo !== undefined)
|
|
36
|
+
return this.matchLinkedToQuery(query);
|
|
37
|
+
|
|
38
|
+
if (query.conditions !== undefined)
|
|
39
|
+
return this.matchComplexQuery(query);
|
|
40
|
+
|
|
41
|
+
if (query.key !== undefined)
|
|
42
|
+
return this.matchSimpleQuery(this.fields[query.key], query);
|
|
43
|
+
|
|
44
|
+
if (!hasProperties(query))
|
|
45
|
+
return true;
|
|
46
|
+
|
|
47
|
+
throw new Error('queryMatcher: Unsupported query! Parsed?');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private matchLinkedToQuery (query) {
|
|
51
|
+
if (query.reverse)
|
|
52
|
+
return MAYBE;
|
|
53
|
+
|
|
54
|
+
let match;
|
|
55
|
+
|
|
56
|
+
if (query.linkedTo !== this.parentType)
|
|
57
|
+
match = false;
|
|
58
|
+
else
|
|
59
|
+
match = this.matchSimpleQuery(this.parentId, query.query);
|
|
60
|
+
|
|
61
|
+
if (match === MAYBE)
|
|
62
|
+
return MAYBE;
|
|
63
|
+
|
|
64
|
+
return query.not ? !match : match;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private matchComplexQuery (query) {
|
|
68
|
+
const conditions = query.conditions;
|
|
69
|
+
|
|
70
|
+
let result;
|
|
71
|
+
let terminal;
|
|
72
|
+
|
|
73
|
+
switch (query.operator) {
|
|
74
|
+
case 'AND':
|
|
75
|
+
result = true;
|
|
76
|
+
terminal = false;
|
|
77
|
+
break;
|
|
78
|
+
case 'OR':
|
|
79
|
+
result = false;
|
|
80
|
+
terminal = true;
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
throw new Error('queryMatcher: Unsupported operator');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (const condition of conditions) {
|
|
87
|
+
const match = this.matchQuery(condition);
|
|
88
|
+
|
|
89
|
+
if (match === terminal)
|
|
90
|
+
return terminal;
|
|
91
|
+
|
|
92
|
+
if (match === MAYBE)
|
|
93
|
+
result = MAYBE;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private matchSimpleQuery (value, query) {
|
|
100
|
+
const key = query.key;
|
|
101
|
+
let queryValue = query.value;
|
|
102
|
+
|
|
103
|
+
if (!hasProperties(query)) // Could be linkedTo an empty object
|
|
104
|
+
return true;
|
|
105
|
+
|
|
106
|
+
if (value === undefined) {
|
|
107
|
+
console.warn('Required field', key, 'not found for', this.itemType);
|
|
108
|
+
return MAYBE;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (queryValue === 'NOW')
|
|
112
|
+
queryValue = moment();
|
|
113
|
+
|
|
114
|
+
switch (query.comparator) {
|
|
115
|
+
case '=':
|
|
116
|
+
return this.compareValues(value, queryValue);
|
|
117
|
+
case '!=':
|
|
118
|
+
return !this.compareValues(value, queryValue);
|
|
119
|
+
case '<':
|
|
120
|
+
return value < queryValue;
|
|
121
|
+
case '<=':
|
|
122
|
+
return value <= queryValue;
|
|
123
|
+
case '>':
|
|
124
|
+
return value > queryValue;
|
|
125
|
+
case '>=':
|
|
126
|
+
return value >= queryValue;
|
|
127
|
+
case 'IN':
|
|
128
|
+
return inArray(value, queryValue);
|
|
129
|
+
case 'NOT IN':
|
|
130
|
+
return !inArray(value, queryValue);
|
|
131
|
+
default:
|
|
132
|
+
throw new Error('queryMatcher: Unsupported comparator: ' + query.comparator + ' in query ' + query);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import * as moment from 'moment';
|
|
2
|
+
import { getFirstProperty, hasProperties } from "./Util";
|
|
3
|
+
|
|
4
|
+
enum CONSTANTS {
|
|
5
|
+
OBJ = 0,
|
|
6
|
+
STR = 1,
|
|
7
|
+
SEP = ':'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default class QueryParser {
|
|
11
|
+
query: any;
|
|
12
|
+
parameters: any;
|
|
13
|
+
|
|
14
|
+
constructor (query, parameters) {
|
|
15
|
+
this.query = query;
|
|
16
|
+
this.parameters = parameters;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
private processValue (value) {
|
|
20
|
+
// algorithm is not important, as long as all arrays are sorted the same way
|
|
21
|
+
if (Array.isArray(value))
|
|
22
|
+
return value.slice().sort();
|
|
23
|
+
|
|
24
|
+
if (moment.isMoment(value))
|
|
25
|
+
return moment(value);
|
|
26
|
+
|
|
27
|
+
if (moment.isDuration(value))
|
|
28
|
+
return moment.duration(value);
|
|
29
|
+
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private compareParsedQueries (a, b) {
|
|
34
|
+
if (a[CONSTANTS.STR] === b[CONSTANTS.STR])
|
|
35
|
+
return 0;
|
|
36
|
+
|
|
37
|
+
return a[CONSTANTS.STR] > b[CONSTANTS.STR] ? 1 : -1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
parse (query = this.query) {
|
|
41
|
+
if (query.linkedTo !== undefined)
|
|
42
|
+
return this.parseLinkedToQuery(
|
|
43
|
+
query.linkedTo,
|
|
44
|
+
query.query,
|
|
45
|
+
query.reverse,
|
|
46
|
+
query.not
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (query.not !== undefined)
|
|
50
|
+
throw new Error('queryParser: "Not" not supported');
|
|
51
|
+
|
|
52
|
+
if (query.custom !== undefined)
|
|
53
|
+
return this.parseCustomQuery(
|
|
54
|
+
query.custom,
|
|
55
|
+
query.parameters
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
if (query.search !== undefined)
|
|
59
|
+
return this.parseSearchQuery(query.search);
|
|
60
|
+
|
|
61
|
+
if (query.conditions !== undefined)
|
|
62
|
+
return this.parseComplexQuery(
|
|
63
|
+
query.conditions,
|
|
64
|
+
query.operator
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (query.key !== undefined)
|
|
68
|
+
return this.parseSimpleQuery(
|
|
69
|
+
query.key,
|
|
70
|
+
query.value,
|
|
71
|
+
query.comparator
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!hasProperties(query))
|
|
75
|
+
return [{}, '{}'];
|
|
76
|
+
|
|
77
|
+
const key = getFirstProperty(query);
|
|
78
|
+
|
|
79
|
+
return this.parseSimpleQuery(key, query[key]);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private parseSearchQuery (value) {
|
|
83
|
+
value = this.formatValue(value);
|
|
84
|
+
|
|
85
|
+
return [
|
|
86
|
+
{search: value[CONSTANTS.OBJ]},
|
|
87
|
+
'[' + 'search' + CONSTANTS.SEP + value[CONSTANTS.STR] + ']'
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private parseCustomQuery (custom, parameters) {
|
|
92
|
+
if (parameters === undefined)
|
|
93
|
+
return [
|
|
94
|
+
{custom: custom},
|
|
95
|
+
'[' + custom + ']'
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
const parsedParameters = this.parseCustomParameters(parameters);
|
|
99
|
+
|
|
100
|
+
return [
|
|
101
|
+
{custom: custom, parameters: parsedParameters[CONSTANTS.OBJ]},
|
|
102
|
+
'[' + custom + CONSTANTS.SEP + parsedParameters[CONSTANTS.STR] + ']'
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private parseCustomParameters (parameters) {
|
|
107
|
+
const keys = Object.keys(parameters).sort();
|
|
108
|
+
|
|
109
|
+
const query = {};
|
|
110
|
+
let string = '';
|
|
111
|
+
|
|
112
|
+
for (let i = 0; i < keys.length; i++) {
|
|
113
|
+
const key = keys[i];
|
|
114
|
+
const value = this.formatValue(parameters[key]);
|
|
115
|
+
|
|
116
|
+
query[key] = value[CONSTANTS.OBJ];
|
|
117
|
+
|
|
118
|
+
if (i > 0)
|
|
119
|
+
string += CONSTANTS.SEP;
|
|
120
|
+
|
|
121
|
+
string += value[CONSTANTS.STR];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return [query, string];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private parseSimpleQuery (key, value, comparator?) {
|
|
128
|
+
value = this.formatValue(value);
|
|
129
|
+
|
|
130
|
+
if (comparator === undefined)
|
|
131
|
+
comparator = Array.isArray(value[CONSTANTS.OBJ]) ? 'IN' : '=';
|
|
132
|
+
|
|
133
|
+
return [
|
|
134
|
+
{key: key, value: value[CONSTANTS.OBJ], comparator: comparator},
|
|
135
|
+
'[' + comparator + CONSTANTS.SEP + key + CONSTANTS.SEP + value[CONSTANTS.STR] + ']'
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private parseLinkedToQuery (linkedTo, query, reverse, not) {
|
|
140
|
+
if (query === undefined)
|
|
141
|
+
query = {};
|
|
142
|
+
|
|
143
|
+
if (reverse === undefined)
|
|
144
|
+
reverse = false;
|
|
145
|
+
|
|
146
|
+
if (not === undefined)
|
|
147
|
+
not = false;
|
|
148
|
+
|
|
149
|
+
const parsedQuery = this.parse(query);
|
|
150
|
+
|
|
151
|
+
return [
|
|
152
|
+
{linkedTo: linkedTo, query: parsedQuery[CONSTANTS.OBJ], reverse: reverse, not: not},
|
|
153
|
+
'[' + 'linkedTo' + CONSTANTS.SEP + linkedTo + CONSTANTS.SEP + parsedQuery[CONSTANTS.STR] + CONSTANTS.SEP + reverse + CONSTANTS.SEP + not + ']'
|
|
154
|
+
];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private parseComplexQuery (conditions, operator) {
|
|
158
|
+
if (operator === undefined)
|
|
159
|
+
operator = 'AND';
|
|
160
|
+
else
|
|
161
|
+
operator = operator.toUpperCase();
|
|
162
|
+
|
|
163
|
+
const parsedConditions = this.parseConditions(conditions);
|
|
164
|
+
|
|
165
|
+
return [
|
|
166
|
+
{conditions: parsedConditions[CONSTANTS.OBJ], operator: operator},
|
|
167
|
+
'[' + operator + parsedConditions[CONSTANTS.STR] + ']'
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private parseConditions (conditions) {
|
|
172
|
+
let parsedConditions = [];
|
|
173
|
+
const query = [];
|
|
174
|
+
let string = '';
|
|
175
|
+
|
|
176
|
+
for (const condition of conditions)
|
|
177
|
+
parsedConditions.push(this.parse(condition));
|
|
178
|
+
|
|
179
|
+
parsedConditions = parsedConditions.sort(this.compareParsedQueries);
|
|
180
|
+
|
|
181
|
+
for (const parsedCondition of parsedConditions) {
|
|
182
|
+
query.push(parsedCondition[CONSTANTS.OBJ]);
|
|
183
|
+
string += parsedCondition[CONSTANTS.STR];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return [query, string];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private formatValue (value) {
|
|
190
|
+
let processedValue;
|
|
191
|
+
|
|
192
|
+
if (typeof value === 'string' && value.charAt(0) === '&') {
|
|
193
|
+
const key = value.substring(1);
|
|
194
|
+
|
|
195
|
+
value = this.parameters[key];
|
|
196
|
+
|
|
197
|
+
if (value === undefined)
|
|
198
|
+
throw new Error('queryParser: Undefined template parameter \'' + key + '\'');
|
|
199
|
+
|
|
200
|
+
processedValue = this.processValue(value);
|
|
201
|
+
|
|
202
|
+
return [processedValue, key + '>' + value];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
processedValue = this.processValue(value);
|
|
206
|
+
|
|
207
|
+
return [processedValue, '' + processedValue];
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export default class QueryService {
|
|
2
|
+
constructor () {
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
addAndCondition (query, condition) {
|
|
6
|
+
if (query.conditions === undefined || query.operator === 'or')
|
|
7
|
+
return {conditions: [query, condition]};
|
|
8
|
+
|
|
9
|
+
// we have an 'and' condition
|
|
10
|
+
return {conditions: query.conditions.concat(condition)};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
addOrCondition (query, condition) {
|
|
14
|
+
if (query.operator === 'or')
|
|
15
|
+
return {conditions: query.conditions.concat(condition)};
|
|
16
|
+
|
|
17
|
+
// we have an 'and' condition
|
|
18
|
+
return {conditions: [query, condition], operator: 'or'};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
addParentCondition (query, parent) {
|
|
22
|
+
let linkedToQuery;
|
|
23
|
+
|
|
24
|
+
if (parent === null)
|
|
25
|
+
return query;
|
|
26
|
+
|
|
27
|
+
linkedToQuery = {linkedTo: parent.itemType, query: parent.itemId};
|
|
28
|
+
|
|
29
|
+
return query === null ? linkedToQuery : this.addAndCondition(query, linkedToQuery);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
createNotIn (idArray) {
|
|
33
|
+
return {comparator: 'NOT IN', key: 'id', value: idArray};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import ColumnUtil from "./ColumnUtil";
|
|
2
|
+
|
|
3
|
+
export default class RequestColumnExpansion {
|
|
4
|
+
private columns = {};
|
|
5
|
+
private columnInfoIdMap = {};
|
|
6
|
+
|
|
7
|
+
constructor (
|
|
8
|
+
private columnUtil: ColumnUtil
|
|
9
|
+
) {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
expand (columnInfo) {
|
|
13
|
+
if (this.columnInfoIdMap[columnInfo.id])
|
|
14
|
+
return;
|
|
15
|
+
|
|
16
|
+
const itemType = columnInfo.itemType;
|
|
17
|
+
|
|
18
|
+
if (this.columns[itemType] === undefined)
|
|
19
|
+
this.columns[itemType] = [];
|
|
20
|
+
|
|
21
|
+
this.columns[itemType] = this.columnUtil
|
|
22
|
+
.mergeRequestColumns(this.columns[itemType], columnInfo.request)
|
|
23
|
+
.merged;
|
|
24
|
+
|
|
25
|
+
this.columnInfoIdMap[columnInfo.id] = true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get () {
|
|
29
|
+
return this.columns;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import ChangesProcessorFactory from "../factories/ChangesProcessorFactory";
|
|
2
|
+
import CollectionCacheFactory from "../factories/CollectionCacheFactory";
|
|
3
|
+
import CollectionResourceFactory from "../factories/CollectionResourceFactory";
|
|
4
|
+
import ItemCacheFactory from "../factories/ItemCacheFactory";
|
|
5
|
+
import ItemResourceFactory from "../factories/ItemResourceFactory";
|
|
6
|
+
import ApiRequestServiceInterface from '../interfaces/ApiRequestServiceInterface';
|
|
7
|
+
import ErrorHandlerInterface from '../interfaces/ErrorHandlerInterface';
|
|
8
|
+
import CollectionEntry from "../models/CollectionEntry";
|
|
9
|
+
import CollectionResource from "../models/CollectionResource";
|
|
10
|
+
import ItemEntry from "../models/ItemEntry";
|
|
11
|
+
import ItemResource from "../models/ItemResource";
|
|
12
|
+
import CollectionCache from "./CollectionCache";
|
|
13
|
+
import CollectionHandler from "./CollectionHandler";
|
|
14
|
+
import ColumnHandler from "./ColumnHandler";
|
|
15
|
+
import ItemCache from './ItemCache';
|
|
16
|
+
import ItemEntryService from './ItemEntryService';
|
|
17
|
+
import ItemHandler from "./ItemHandler";
|
|
18
|
+
import ItemResourceService from './ItemResourceService';
|
|
19
|
+
import LockService from "./LockService";
|
|
20
|
+
import PromiseService from "./PromiseService";
|
|
21
|
+
import { extend } from './Util';
|
|
22
|
+
|
|
23
|
+
export default class ResourceCache {
|
|
24
|
+
collections: CollectionCache;
|
|
25
|
+
deferredCollections: CollectionCache;
|
|
26
|
+
items: ItemCache;
|
|
27
|
+
collectionOptions = {};
|
|
28
|
+
tabRequestParameters = {};
|
|
29
|
+
callbacks = {onBeforeSave: {}, onSaveSuccess: {}, onSaveWithoutClosing: {}};
|
|
30
|
+
|
|
31
|
+
changesInterval: null | number;
|
|
32
|
+
_hasChanges = false;
|
|
33
|
+
rootItem: ItemEntry;
|
|
34
|
+
|
|
35
|
+
constructor (
|
|
36
|
+
private apiRequestService: ApiRequestServiceInterface,
|
|
37
|
+
private errorHandler: ErrorHandlerInterface,
|
|
38
|
+
private itemHandler: ItemHandler,
|
|
39
|
+
private collectionHandler: CollectionHandler,
|
|
40
|
+
private columnHandler: ColumnHandler,
|
|
41
|
+
private lockService: LockService,
|
|
42
|
+
private itemResourceFactory: ItemResourceFactory,
|
|
43
|
+
private collectionCacheFactory: CollectionCacheFactory,
|
|
44
|
+
private changesProcessorFactory: ChangesProcessorFactory,
|
|
45
|
+
private itemCacheFactory: ItemCacheFactory,
|
|
46
|
+
private collectionResourceFactory: CollectionResourceFactory,
|
|
47
|
+
private itemEntryService: ItemEntryService,
|
|
48
|
+
private itemResourceService: ItemResourceService,
|
|
49
|
+
private promiseService: PromiseService
|
|
50
|
+
) {
|
|
51
|
+
this.collections = this.collectionCacheFactory.construct();
|
|
52
|
+
this.deferredCollections = this.collectionCacheFactory.construct();
|
|
53
|
+
this.items = this.itemCacheFactory.construct();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
addSingleItem (itemEntry: ItemEntry, options, isRoot) {
|
|
57
|
+
if (isRoot)
|
|
58
|
+
this.rootItem = itemEntry;
|
|
59
|
+
|
|
60
|
+
if (!this.items.contains(itemEntry.itemType, itemEntry.itemId))
|
|
61
|
+
this.items.add(itemEntry);
|
|
62
|
+
|
|
63
|
+
options = this.itemResourceService.processOptions(options);
|
|
64
|
+
options.onDeactivate = this.clearItem;
|
|
65
|
+
|
|
66
|
+
return this.itemResourceFactory.construct(itemEntry, options);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
addCollection (collectionEntry: CollectionEntry, resourceOptions) {
|
|
70
|
+
if (!this.collections.contains(collectionEntry)) {
|
|
71
|
+
this.collections.add(collectionEntry);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const collectionEntryId = collectionEntry.id;
|
|
75
|
+
|
|
76
|
+
if (this.collectionOptions[collectionEntryId] === undefined)
|
|
77
|
+
this.collectionOptions[collectionEntryId] = {
|
|
78
|
+
edit: resourceOptions.editCollection,
|
|
79
|
+
editItems: resourceOptions.editItems
|
|
80
|
+
};
|
|
81
|
+
else {
|
|
82
|
+
const collectionOptionsEntry = this.collectionOptions[collectionEntryId];
|
|
83
|
+
|
|
84
|
+
collectionOptionsEntry.edit = collectionOptionsEntry.edit || resourceOptions.editCollection;
|
|
85
|
+
collectionOptionsEntry.editItems = collectionOptionsEntry.editItems || resourceOptions.editItems;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
resourceOptions.onDeactivateItem = this.clearItem;
|
|
89
|
+
resourceOptions.onDeactivateCollection = this.clearCollection;
|
|
90
|
+
|
|
91
|
+
return this.collectionResourceFactory.construct(collectionEntry, resourceOptions);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
clearCollection (collectionResource: CollectionResource) {
|
|
95
|
+
const collectionEntry: CollectionEntry = collectionResource.entry;
|
|
96
|
+
|
|
97
|
+
this.collections.clear(collectionEntry);
|
|
98
|
+
collectionEntry.clearResources();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
clearItem (itemResource: ItemResource) {
|
|
102
|
+
const itemEntry: ItemEntry = itemResource.entry;
|
|
103
|
+
|
|
104
|
+
this.items.clear(itemEntry);
|
|
105
|
+
this.itemEntryService.clearResources(itemEntry);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
clearResources (isSubTabChange = false) {
|
|
109
|
+
this.items.forEach(this.itemEntryService.clearResources);
|
|
110
|
+
|
|
111
|
+
this.collections.forEach(collectionEntry => {
|
|
112
|
+
collectionEntry.clearResources(isSubTabChange);
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// TODO: remove this condition once crew and transport are no longer loaded in seperate grid instances in the project planner grid
|
|
117
|
+
if (!isSubTabChange)
|
|
118
|
+
this.collections.clear();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
updateSingleItems (conversionMap) {
|
|
122
|
+
for (const itemType in conversionMap) {
|
|
123
|
+
for (const oldItemId in conversionMap[itemType]) {
|
|
124
|
+
const itemEntry = this.items.get(itemType, oldItemId);
|
|
125
|
+
|
|
126
|
+
if (itemEntry === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
|
|
129
|
+
this.items.updateItemId(itemEntry, oldItemId);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// resources will be cleared later (clear function)
|
|
135
|
+
revertChanges () {
|
|
136
|
+
this.items.forEach(this.itemEntryService.resetModified);
|
|
137
|
+
this.collections.forEach(this.resetCollection);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
resetCollection (collectionEntry: CollectionEntry) {
|
|
141
|
+
const options = this.collectionOptions[collectionEntry.id];
|
|
142
|
+
|
|
143
|
+
if (options.edit)
|
|
144
|
+
collectionEntry.revertChanges();
|
|
145
|
+
|
|
146
|
+
if (!options.editItems)
|
|
147
|
+
return;
|
|
148
|
+
|
|
149
|
+
// these are edit resources (editItems)
|
|
150
|
+
for (const collectionItem of collectionEntry.items)
|
|
151
|
+
this.itemEntryService.resetModified(collectionItem);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
hasChanges () {
|
|
155
|
+
if (!this._hasChanges)
|
|
156
|
+
this._hasChanges = this.detectChanges();
|
|
157
|
+
|
|
158
|
+
return this._hasChanges;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
detectChanges () {
|
|
162
|
+
if (this.items.some(this.itemEntryService.hasChanges))
|
|
163
|
+
return true;
|
|
164
|
+
|
|
165
|
+
return this.collections.some((collectionEntry: CollectionEntry) => {
|
|
166
|
+
this.collectionHasChanges(collectionEntry);
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
collectionHasChanges (collectionEntry: CollectionEntry) {
|
|
171
|
+
const options = this.collectionOptions[collectionEntry.id];
|
|
172
|
+
|
|
173
|
+
if (options.edit && (collectionEntry.hasAddedItems() || collectionEntry.hasRemovedItems()))
|
|
174
|
+
return true;
|
|
175
|
+
|
|
176
|
+
return options.editItems && collectionEntry.hasUpdatedItems();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
isLockable () {
|
|
180
|
+
if (this.rootItem === undefined)
|
|
181
|
+
return false;
|
|
182
|
+
|
|
183
|
+
return this.itemEntryService.isLockable(this.rootItem);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
save (closeTab = false, parameters, breakUpByParent, onAfterCreateCallback) {
|
|
187
|
+
return (this.isLockable() ? this.lockService.fetchLock(this.rootItem) : this.promiseService.when()).then(
|
|
188
|
+
lockToken => {
|
|
189
|
+
return this.executeCallbacks('onBeforeSave', closeTab);
|
|
190
|
+
},
|
|
191
|
+
this.errorHandler.reject
|
|
192
|
+
).then(
|
|
193
|
+
() => {
|
|
194
|
+
const requestParameters = extend({}, this.tabRequestParameters, parameters);
|
|
195
|
+
|
|
196
|
+
this.tabRequestParameters = {};
|
|
197
|
+
|
|
198
|
+
return this.changesProcessorFactory.construct().execute(
|
|
199
|
+
this.items.toArray(),
|
|
200
|
+
this.collections.toArray(),
|
|
201
|
+
breakUpByParent,
|
|
202
|
+
requestParameters,
|
|
203
|
+
this.collectionOptions,
|
|
204
|
+
onAfterCreateCallback
|
|
205
|
+
);
|
|
206
|
+
},
|
|
207
|
+
this.errorHandler.reject
|
|
208
|
+
).then(conversionMap => {
|
|
209
|
+
if (this.isLockable())
|
|
210
|
+
this.lockService.clearLock(this.rootItem);
|
|
211
|
+
|
|
212
|
+
this._hasChanges = false;
|
|
213
|
+
|
|
214
|
+
if (!closeTab)
|
|
215
|
+
this.updateSingleItems(conversionMap);
|
|
216
|
+
|
|
217
|
+
return this.executeCallbacks('onSaveSuccess', conversionMap).then(
|
|
218
|
+
() => {
|
|
219
|
+
if (!closeTab)
|
|
220
|
+
this.executeCallbacks('onSaveWithoutClosing', conversionMap);
|
|
221
|
+
|
|
222
|
+
return conversionMap;
|
|
223
|
+
},
|
|
224
|
+
this.errorHandler.reject
|
|
225
|
+
);
|
|
226
|
+
},
|
|
227
|
+
this.errorHandler.reject
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
executeCallbacks (event, callbackParameter) {
|
|
232
|
+
const result = {};
|
|
233
|
+
|
|
234
|
+
for (const key in this.callbacks[event])
|
|
235
|
+
result[key] = this.callbacks[event][key](callbackParameter);
|
|
236
|
+
|
|
237
|
+
return this.promiseService.all(result);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
enableChangesWatcher () {
|
|
241
|
+
if (this.changesInterval !== null) {
|
|
242
|
+
console.warn('Changes watcher already running!', this);
|
|
243
|
+
this.disableChangesWatcher();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
this.changesInterval = setInterval(
|
|
247
|
+
() => {
|
|
248
|
+
if (!this.hasChanges())
|
|
249
|
+
return;
|
|
250
|
+
|
|
251
|
+
this.disableChangesWatcher();
|
|
252
|
+
|
|
253
|
+
if (this.isLockable())
|
|
254
|
+
this.lockService.fetchLock(this.rootItem);
|
|
255
|
+
},
|
|
256
|
+
3000
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* @deprecated
|
|
262
|
+
*/
|
|
263
|
+
enableWatcher () {
|
|
264
|
+
this.enableChangesWatcher();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
disableChangesWatcher () {
|
|
268
|
+
clearInterval(this.changesInterval);
|
|
269
|
+
this.changesInterval = null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @deprecated
|
|
274
|
+
*/
|
|
275
|
+
disableWatcher () {
|
|
276
|
+
this.disableChangesWatcher();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* @deprecated
|
|
281
|
+
*/
|
|
282
|
+
registerCallback (event: string, key: string, callback) {
|
|
283
|
+
this.callbacks[event][key] = callback;
|
|
284
|
+
|
|
285
|
+
return () => {
|
|
286
|
+
delete this.callbacks[event][key];
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|