@onehat/data 1.21.19 → 1.22.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/package.json +3 -2
- package/src/Entity/Entity.js +6 -6
- package/src/Repository/Ajax.js +21 -10
- package/src/Repository/OneBuild.js +23 -415
- package/src/Repository/Repository.js +0 -89
- package/src/Repository/Tree.js +444 -0
- package/src/Repository/OneBuild2.js +0 -953
|
@@ -2127,95 +2127,6 @@ export default class Repository extends EventEmitter {
|
|
|
2127
2127
|
}
|
|
2128
2128
|
|
|
2129
2129
|
|
|
2130
|
-
// ______
|
|
2131
|
-
// /_ __/_______ ___ _____
|
|
2132
|
-
// / / / ___/ _ \/ _ \/ ___/
|
|
2133
|
-
// / / / / / __/ __(__ )
|
|
2134
|
-
// /_/ /_/ \___/\___/____/
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
/**
|
|
2138
|
-
* Gets the root TreeNodes
|
|
2139
|
-
*/
|
|
2140
|
-
getRootNodes() {
|
|
2141
|
-
this.ensureTree();
|
|
2142
|
-
if (this.isDestroyed) {
|
|
2143
|
-
this.throwError('this.loadRootNodes is no longer valid. Repository has been destroyed.');
|
|
2144
|
-
return;
|
|
2145
|
-
}
|
|
2146
|
-
|
|
2147
|
-
// Look through all entities and pull out the root nodes.
|
|
2148
|
-
// Subclasses of Repository will override this method to get root nodes from server
|
|
2149
|
-
const entities = _.filter(this.getEntities(), (entity) => {
|
|
2150
|
-
return entity.isRoot;
|
|
2151
|
-
})
|
|
2152
|
-
return entities;
|
|
2153
|
-
}
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
/**
|
|
2157
|
-
* Populates the TreeNodes with .parent and .children references
|
|
2158
|
-
*/
|
|
2159
|
-
assembleTreeNodes() {
|
|
2160
|
-
this.ensureTree();
|
|
2161
|
-
if (this.isDestroyed) {
|
|
2162
|
-
this.throwError('this.assembleTreeNodes is no longer valid. Repository has been destroyed.');
|
|
2163
|
-
return;
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
|
-
const treeNodes = this.getEntities();
|
|
2167
|
-
|
|
2168
|
-
// Reset all parent/child relationships
|
|
2169
|
-
_.each(treeNodes, (treeNode) => {
|
|
2170
|
-
treeNode.parent = null;
|
|
2171
|
-
treeNode.children = [];
|
|
2172
|
-
});
|
|
2173
|
-
|
|
2174
|
-
// Rebuild all parent/child relationships
|
|
2175
|
-
const oThis = this;
|
|
2176
|
-
_.each(treeNodes, (treeNode) => {
|
|
2177
|
-
const parent = oThis.getById(treeNode.parentId);
|
|
2178
|
-
if (parent) {
|
|
2179
|
-
treeNode.parent = parent;
|
|
2180
|
-
parent.children.push(treeNode);
|
|
2181
|
-
}
|
|
2182
|
-
});
|
|
2183
|
-
}
|
|
2184
|
-
|
|
2185
|
-
/**
|
|
2186
|
-
* Removes the treeNode and all of its children from repository
|
|
2187
|
-
* without deleting anything on the server
|
|
2188
|
-
*/
|
|
2189
|
-
removeTreeNode(treeNode) {
|
|
2190
|
-
if (!_.isEmpty(treeNode.children)) {
|
|
2191
|
-
const children = treeNode.children;
|
|
2192
|
-
treeNode.parent = null;
|
|
2193
|
-
treeNode.children = [];
|
|
2194
|
-
|
|
2195
|
-
const oThis = this;
|
|
2196
|
-
_.each(children, (child) => {
|
|
2197
|
-
oThis.removeTreeNode(child);
|
|
2198
|
-
});
|
|
2199
|
-
}
|
|
2200
|
-
|
|
2201
|
-
this.removeEntity(treeNode);
|
|
2202
|
-
}
|
|
2203
|
-
|
|
2204
|
-
/**
|
|
2205
|
-
* Helper to make sure this Repository is a tree
|
|
2206
|
-
* @private
|
|
2207
|
-
*/
|
|
2208
|
-
async ensureTree() {
|
|
2209
|
-
if (!this.isTree) {
|
|
2210
|
-
this.throwError('This Repository is not a tree!');
|
|
2211
|
-
return false;
|
|
2212
|
-
}
|
|
2213
|
-
return true;
|
|
2214
|
-
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
2130
|
|
|
2220
2131
|
// __ ____ _ ___ __ _
|
|
2221
2132
|
// / / / / /_(_) (_) /_(_)__ _____
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/** @module Repository */
|
|
2
|
+
|
|
3
|
+
import OneBuildRepository from './OneBuild.js';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This class is used for OneBuild Trees
|
|
9
|
+
* that contain multiple node types.
|
|
10
|
+
*
|
|
11
|
+
* @extends TreeRepository
|
|
12
|
+
*/
|
|
13
|
+
class TreeRepository extends OneBuildRepository {
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
|
|
17
|
+
const defaults = {
|
|
18
|
+
|
|
19
|
+
isTree: true,
|
|
20
|
+
rootNodeType: this._getModel(), // e.g. 'Fleets'
|
|
21
|
+
|
|
22
|
+
api: {
|
|
23
|
+
getNodes: 'getNodes',
|
|
24
|
+
moveNode: 'moveNode',
|
|
25
|
+
searchNodes: 'searchNodes',
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// TODO: modify all tree methods to handle multiple node types
|
|
29
|
+
// SOME are updated, but many are not.
|
|
30
|
+
// I'll need to modify all generated models to use TreeRepository instead of OneBuild with isTree: true.
|
|
31
|
+
|
|
32
|
+
};
|
|
33
|
+
_.merge(this, defaults, config);
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async initialize() {
|
|
38
|
+
|
|
39
|
+
this.registerEvents([
|
|
40
|
+
'loadRootNodes',
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
await super.initialize();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getModelFromTreeNode(treeNode) {
|
|
47
|
+
return treeNode.nodeType || this.rootNodeType;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Loads the root nodes of this tree.
|
|
52
|
+
*/
|
|
53
|
+
loadRootNodes(depth) {
|
|
54
|
+
this.ensureTree();
|
|
55
|
+
if (this.isDestroyed) {
|
|
56
|
+
this.throwError('this.setRootNode is no longer valid. Repository has been destroyed.');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!this.isOnline) {
|
|
60
|
+
this.throwError('Offline');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.emit('beforeLoad'); // TODO: canceling beforeLoad will cancel the load operation
|
|
65
|
+
this.markLoading();
|
|
66
|
+
|
|
67
|
+
const
|
|
68
|
+
data = _.merge({ depth }, this._baseParams, this._params),
|
|
69
|
+
url = this.rootNodeType + '/' + this.api.getNodes;
|
|
70
|
+
|
|
71
|
+
if (this.debugMode) {
|
|
72
|
+
console.log('loadRootNodes', data);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this._send('POST', url, data)
|
|
76
|
+
.then((result) => {
|
|
77
|
+
if (this.debugMode) {
|
|
78
|
+
console.log('Response for loadRootNodes', result);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (this.isDestroyed) {
|
|
82
|
+
// If this repository gets destroyed before it has a chance
|
|
83
|
+
// to process the Ajax request, just ignore the response.
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const {
|
|
88
|
+
root,
|
|
89
|
+
success,
|
|
90
|
+
total,
|
|
91
|
+
message
|
|
92
|
+
} = this._processServerResponse(result);
|
|
93
|
+
|
|
94
|
+
if (!success) {
|
|
95
|
+
this.throwError(message);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this._destroyEntities();
|
|
100
|
+
|
|
101
|
+
// Set the current entities
|
|
102
|
+
const oThis = this;
|
|
103
|
+
this.entities = _.map(root, (data) => {
|
|
104
|
+
const entity = Repository._createEntity(oThis.schema, data, this, true);
|
|
105
|
+
oThis._relayEntityEvents(entity);
|
|
106
|
+
return entity;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
this.assembleTreeNodes();
|
|
110
|
+
|
|
111
|
+
// Set the total records that pass filter
|
|
112
|
+
this.total = total;
|
|
113
|
+
this._setPaginationVars();
|
|
114
|
+
|
|
115
|
+
this.areRootNodesLoaded = true;
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
// Don't emit events for root nodes...
|
|
119
|
+
this.rehash();
|
|
120
|
+
this.emit('loadRootNodes', this);
|
|
121
|
+
// this.emit('changeData', this.entities);
|
|
122
|
+
|
|
123
|
+
return this.getBy((entity) => {
|
|
124
|
+
return entity.isRoot;
|
|
125
|
+
});
|
|
126
|
+
})
|
|
127
|
+
.finally(() => {
|
|
128
|
+
this.markLoading(false);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Loads (or reloads) the supplied treeNode
|
|
134
|
+
*/
|
|
135
|
+
loadNode(treeNode, depth = 1) {
|
|
136
|
+
this.ensureTree();
|
|
137
|
+
if (this.isDestroyed) {
|
|
138
|
+
this.throwError('this.loadNode is no longer valid. Repository has been destroyed.');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (!this.isOnline) {
|
|
142
|
+
this.throwError('Offline');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// If children already exist, remove them from the repository
|
|
147
|
+
// This way, we can reload just a portion of the tree
|
|
148
|
+
if (!_.isEmpty(treeNode.children)) {
|
|
149
|
+
_.each(treeNode.children, (child) => {
|
|
150
|
+
treeNode.repository.removeTreeNode(child);
|
|
151
|
+
});
|
|
152
|
+
treeNode.children = [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
this.markLoading();
|
|
156
|
+
|
|
157
|
+
const
|
|
158
|
+
data = _.merge({ depth, nodeId: treeNode.id, }, this._baseParams, this._params),
|
|
159
|
+
url = this.getModelFromTreeNode(treeNode) + '/' + this.api.getNodes;
|
|
160
|
+
|
|
161
|
+
if (this.debugMode) {
|
|
162
|
+
console.log('loadNode', data);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return this._send('POST', url, data)
|
|
166
|
+
.then((result) => {
|
|
167
|
+
if (this.debugMode) {
|
|
168
|
+
console.log('Response for loadNode', result);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (this.isDestroyed) {
|
|
172
|
+
// If this repository gets destroyed before it has a chance
|
|
173
|
+
// to process the Ajax request, just ignore the response.
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const {
|
|
178
|
+
root,
|
|
179
|
+
success,
|
|
180
|
+
total,
|
|
181
|
+
message
|
|
182
|
+
} = this._processServerResponse(result);
|
|
183
|
+
|
|
184
|
+
if (!success) {
|
|
185
|
+
this.throwError(message);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Set the current entities
|
|
190
|
+
const oThis = this;
|
|
191
|
+
const children = _.map(root, (data) => {
|
|
192
|
+
const entity = Repository._createEntity(oThis.schema, data, this, true);
|
|
193
|
+
oThis._relayEntityEvents(entity);
|
|
194
|
+
return entity;
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
this.entities = this.entities.concat(children);
|
|
198
|
+
|
|
199
|
+
this.assembleTreeNodes();
|
|
200
|
+
|
|
201
|
+
this._setPaginationVars();
|
|
202
|
+
|
|
203
|
+
this.rehash();
|
|
204
|
+
// this.emit('changeData', this.entities);
|
|
205
|
+
this.emit('load', this);
|
|
206
|
+
|
|
207
|
+
return children;
|
|
208
|
+
})
|
|
209
|
+
.finally(() => {
|
|
210
|
+
this.markLoading(false);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* alias for backward compatibility
|
|
216
|
+
*/
|
|
217
|
+
loadChildNodes(treeNode, depth = 1) {
|
|
218
|
+
return this.loadNode(treeNode, depth);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Override the AjaxRepository to we can reload a treeNode if needed
|
|
223
|
+
*/
|
|
224
|
+
reloadEntity(entity, callback = null) {
|
|
225
|
+
if (!entity.isTree) {
|
|
226
|
+
return super.reloadEntity(entity, callback);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return this.loadNode(entity, 1);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Searches all nodes for the supplied text.
|
|
234
|
+
* This basically takes the search query and returns whatever the server sends
|
|
235
|
+
*/
|
|
236
|
+
searchNodes(q) {
|
|
237
|
+
this.ensureTree();
|
|
238
|
+
if (this.isDestroyed) {
|
|
239
|
+
this.throwError('this.searchNodes is no longer valid. Repository has been destroyed.');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!this.isOnline) {
|
|
243
|
+
this.throwError('Offline');
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const
|
|
248
|
+
data = _.merge({ q, }, this._baseParams, this._params),
|
|
249
|
+
url = this.rootNodeType + '/' + this.api.searchNodes;
|
|
250
|
+
|
|
251
|
+
if (this.debugMode) {
|
|
252
|
+
console.log('searchNodes', data);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return this._send('POST', url, data)
|
|
256
|
+
.then((result) => {
|
|
257
|
+
if (this.debugMode) {
|
|
258
|
+
console.log('Response for searchNodes', result);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (this.isDestroyed) {
|
|
262
|
+
// If this repository gets destroyed before it has a chance
|
|
263
|
+
// to process the Ajax request, just ignore the response.
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const {
|
|
268
|
+
root,
|
|
269
|
+
success,
|
|
270
|
+
total,
|
|
271
|
+
message
|
|
272
|
+
} = this._processServerResponse(result);
|
|
273
|
+
|
|
274
|
+
if (!success) {
|
|
275
|
+
this.throwError(message);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return root;
|
|
280
|
+
})
|
|
281
|
+
.finally(() => {
|
|
282
|
+
this.markLoading(false);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Moves the supplied treeNode to a new position on the tree
|
|
288
|
+
* @returns id of common ancestor node
|
|
289
|
+
*/
|
|
290
|
+
moveTreeNode(treeNode, newParentId) {
|
|
291
|
+
this.ensureTree();
|
|
292
|
+
if (this.isDestroyed) {
|
|
293
|
+
this.throwError('this.moveTreeNode is no longer valid. Repository has been destroyed.');
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (!this.isOnline) {
|
|
297
|
+
this.throwError('Offline');
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const
|
|
302
|
+
oldParentId = treeNode.parent?.id,
|
|
303
|
+
data = _.merge({ nodeId: treeNode.id, parentId: newParentId, }, this._baseParams, this._params),
|
|
304
|
+
url = this.rootNodeType + '/' + this.api.moveNode;
|
|
305
|
+
|
|
306
|
+
// NOTE: The rootNodeType controller needs to know about all the possible nodeTypes,
|
|
307
|
+
// so any particular node can move all around the tree.
|
|
308
|
+
|
|
309
|
+
if (this.debugMode) {
|
|
310
|
+
console.log('moveTreeNode', data);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return this._send('POST', url, data)
|
|
314
|
+
.then((result) => {
|
|
315
|
+
if (this.debugMode) {
|
|
316
|
+
console.log('Response for searchNodes', result);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (this.isDestroyed) {
|
|
320
|
+
// If this repository gets destroyed before it has a chance
|
|
321
|
+
// to process the Ajax request, just ignore the response.
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const {
|
|
326
|
+
root: {
|
|
327
|
+
commonAncestorId,
|
|
328
|
+
oldParent,
|
|
329
|
+
newParent,
|
|
330
|
+
node,
|
|
331
|
+
},
|
|
332
|
+
success,
|
|
333
|
+
total,
|
|
334
|
+
message
|
|
335
|
+
} = this._processServerResponse(result);
|
|
336
|
+
|
|
337
|
+
if (!success) {
|
|
338
|
+
this.throwError(message);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// move it from oldParent.children to newParent.children
|
|
343
|
+
const
|
|
344
|
+
oldParentRecord = this.getById(oldParentId),
|
|
345
|
+
newParentRecord = this.getById(newParentId);
|
|
346
|
+
|
|
347
|
+
oldParentRecord?.loadOriginalData(oldParent);
|
|
348
|
+
newParentRecord.loadOriginalData(newParent);
|
|
349
|
+
treeNode.loadOriginalData(node);
|
|
350
|
+
|
|
351
|
+
this.assembleTreeNodes();
|
|
352
|
+
|
|
353
|
+
return commonAncestorId;
|
|
354
|
+
})
|
|
355
|
+
.finally(() => {
|
|
356
|
+
this.markLoading(false);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Gets the root TreeNodes
|
|
362
|
+
*/
|
|
363
|
+
getRootNodes() {
|
|
364
|
+
this.ensureTree();
|
|
365
|
+
if (this.isDestroyed) {
|
|
366
|
+
this.throwError('this.loadRootNodes is no longer valid. Repository has been destroyed.');
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Look through all entities and pull out the root nodes.
|
|
371
|
+
// Subclasses of Repository will override this method to get root nodes from server
|
|
372
|
+
const entities = _.filter(this.getEntities(), (entity) => {
|
|
373
|
+
return entity.isRoot;
|
|
374
|
+
})
|
|
375
|
+
return entities;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Populates the TreeNodes with .parent and .children references
|
|
380
|
+
*/
|
|
381
|
+
assembleTreeNodes() {
|
|
382
|
+
this.ensureTree();
|
|
383
|
+
if (this.isDestroyed) {
|
|
384
|
+
this.throwError('this.assembleTreeNodes is no longer valid. Repository has been destroyed.');
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const treeNodes = this.getEntities();
|
|
389
|
+
|
|
390
|
+
// Reset all parent/child relationships
|
|
391
|
+
_.each(treeNodes, (treeNode) => {
|
|
392
|
+
treeNode.parent = null;
|
|
393
|
+
treeNode.children = [];
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Rebuild all parent/child relationships
|
|
397
|
+
const oThis = this;
|
|
398
|
+
_.each(treeNodes, (treeNode) => {
|
|
399
|
+
const parent = oThis.getById(treeNode.parentId);
|
|
400
|
+
if (parent) {
|
|
401
|
+
treeNode.parent = parent;
|
|
402
|
+
parent.children.push(treeNode);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Removes the treeNode and all of its children from repository
|
|
409
|
+
* without deleting anything on the server
|
|
410
|
+
*/
|
|
411
|
+
removeTreeNode(treeNode) {
|
|
412
|
+
if (!_.isEmpty(treeNode.children)) {
|
|
413
|
+
const children = treeNode.children;
|
|
414
|
+
treeNode.parent = null;
|
|
415
|
+
treeNode.children = [];
|
|
416
|
+
|
|
417
|
+
const oThis = this;
|
|
418
|
+
_.each(children, (child) => {
|
|
419
|
+
oThis.removeTreeNode(child);
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
this.removeEntity(treeNode);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Helper to make sure this Repository is a tree
|
|
428
|
+
* @private
|
|
429
|
+
*/
|
|
430
|
+
async ensureTree() {
|
|
431
|
+
if (!this.isTree) {
|
|
432
|
+
this.throwError('This Repository is not a tree!');
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
TreeRepository.className = 'Tree';
|
|
442
|
+
TreeRepository.type = 'tree';
|
|
443
|
+
|
|
444
|
+
export default TreeRepository;
|