@nymphjs/nymph 1.0.0-beta.11 → 1.0.0-beta.111
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/CHANGELOG.md +540 -0
- package/README.md +23 -21
- package/dist/Entity.d.ts +293 -10
- package/dist/Entity.js +560 -148
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +52 -13
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +256 -23
- package/dist/Entity.types.js +1 -2
- package/dist/Nymph.d.ts +586 -12
- package/dist/Nymph.js +844 -179
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +81 -8
- package/dist/Nymph.types.js +3 -5
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +43 -0
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +26 -3
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +201 -40
- package/dist/driver/NymphDriver.js +685 -239
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/index.d.ts +2 -2
- package/dist/driver/index.js +3 -23
- package/dist/driver/index.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +2 -2
- package/dist/driver/testArtifacts.js +909 -93
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/ClassNotAvailableError.js +1 -5
- package/dist/errors/ClassNotAvailableError.js.map +1 -1
- package/dist/errors/EntityConflictError.js +1 -5
- package/dist/errors/EntityConflictError.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +8 -6
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/errors/EntityIsSleepingReferenceError.js +1 -5
- package/dist/errors/EntityIsSleepingReferenceError.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +7 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/HttpError.d.ts +5 -0
- package/dist/errors/HttpError.js +11 -0
- package/dist/errors/HttpError.js.map +1 -0
- package/dist/errors/InvalidParametersError.js +1 -5
- package/dist/errors/InvalidParametersError.js.map +1 -1
- package/dist/errors/InvalidStateError.d.ts +3 -0
- package/dist/errors/InvalidStateError.js +7 -0
- package/dist/errors/InvalidStateError.js.map +1 -0
- package/dist/errors/NotConfiguredError.js +1 -5
- package/dist/errors/NotConfiguredError.js.map +1 -1
- package/dist/errors/QueryFailedError.d.ts +2 -1
- package/dist/errors/QueryFailedError.js +5 -6
- package/dist/errors/QueryFailedError.js.map +1 -1
- package/dist/errors/UnableToConnectError.js +1 -5
- package/dist/errors/UnableToConnectError.js.map +1 -1
- package/dist/errors/index.d.ts +11 -8
- package/dist/errors/index.js +11 -24
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +14 -34
- package/dist/index.js.map +1 -1
- package/dist/nymphJoiProps.d.ts +1 -1
- package/dist/nymphJoiProps.js +10 -15
- package/dist/nymphJoiProps.js.map +1 -1
- package/dist/testArtifacts.d.ts +19 -6
- package/dist/testArtifacts.js +32 -37
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +27 -12
- package/dist/testMocks.js +40 -29
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +4 -4
- package/dist/utils.js +40 -29
- package/dist/utils.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +24 -20
- package/src/Entity.test.ts +50 -7
- package/src/Entity.ts +489 -150
- package/src/Entity.types.ts +72 -74
- package/src/Nymph.ts +985 -322
- package/src/Nymph.types.ts +150 -27
- package/src/conf/d.ts +25 -0
- package/src/conf/defaults.ts +34 -1
- package/src/conf/index.ts +2 -2
- package/src/driver/NymphDriver.ts +859 -412
- package/src/driver/index.ts +2 -2
- package/src/driver/testArtifacts.ts +1090 -186
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/HttpError.ts +12 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/QueryFailedError.ts +3 -1
- package/src/errors/index.ts +14 -8
- package/src/index.ts +11 -11
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +20 -20
- package/src/testMocks.ts +76 -35
- package/src/type/locutus.d.ts +3 -0
- package/src/utils.ts +39 -18
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import { difference } from 'lodash';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { difference } from 'lodash-es';
|
|
3
3
|
import ReadLines from 'n-readlines';
|
|
4
|
-
import strtotime from 'locutus/php/datetime/strtotime';
|
|
5
|
-
import {
|
|
4
|
+
import strtotime from 'locutus/php/datetime/strtotime.js';
|
|
5
|
+
import { Tokenizer } from '@sciactive/tokenizer';
|
|
6
6
|
|
|
7
|
-
import type Nymph from '../Nymph';
|
|
8
|
-
import {
|
|
7
|
+
import type Nymph from '../Nymph.js';
|
|
8
|
+
import type { Selector, Options, FormattedSelector } from '../Nymph.types.js';
|
|
9
|
+
import type { EntityInstanceType, EntityObjectType } from '../Entity.js';
|
|
10
|
+
import type {
|
|
9
11
|
EntityConstructor,
|
|
10
12
|
EntityData,
|
|
11
13
|
EntityInterface,
|
|
12
14
|
EntityReference,
|
|
13
15
|
SerializedEntityData,
|
|
14
|
-
} from '../Entity.types';
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
} from '../Entity.types.js';
|
|
17
|
+
import {
|
|
18
|
+
InvalidParametersError,
|
|
19
|
+
UnableToConnectError,
|
|
20
|
+
} from '../errors/index.js';
|
|
21
|
+
import { xor } from '../utils.js';
|
|
18
22
|
|
|
19
23
|
// from: https://stackoverflow.com/a/6969486/664915
|
|
20
24
|
function escapeRegExp(string: string): string {
|
|
@@ -28,11 +32,13 @@ export default abstract class NymphDriver {
|
|
|
28
32
|
protected nymph: Nymph = new Proxy({} as Nymph, {
|
|
29
33
|
get() {
|
|
30
34
|
throw new Error(
|
|
31
|
-
'Attempted access of Nymph instance before driver initialization!'
|
|
35
|
+
'Attempted access of Nymph instance before driver initialization!',
|
|
32
36
|
);
|
|
33
37
|
},
|
|
34
38
|
});
|
|
35
39
|
|
|
40
|
+
protected tokenizer = new Tokenizer();
|
|
41
|
+
|
|
36
42
|
/**
|
|
37
43
|
* A cache to make entity retrieval faster.
|
|
38
44
|
*/
|
|
@@ -67,13 +73,14 @@ export default abstract class NymphDriver {
|
|
|
67
73
|
abstract connect(): Promise<boolean>;
|
|
68
74
|
|
|
69
75
|
abstract isConnected(): boolean;
|
|
76
|
+
protected abstract internalTransaction(name: string): Promise<any>;
|
|
70
77
|
abstract startTransaction(name: string): Promise<Nymph>;
|
|
71
78
|
abstract commit(name: string): Promise<boolean>;
|
|
72
79
|
abstract rollback(name: string): Promise<boolean>;
|
|
73
80
|
abstract inTransaction(): Promise<boolean>;
|
|
74
81
|
abstract deleteEntityByID(
|
|
75
82
|
guid: string,
|
|
76
|
-
classConstructor: EntityConstructor
|
|
83
|
+
classConstructor: EntityConstructor,
|
|
77
84
|
): Promise<boolean>;
|
|
78
85
|
abstract deleteEntityByID(guid: string, className?: string): Promise<boolean>;
|
|
79
86
|
abstract deleteUID(name: string): Promise<boolean>;
|
|
@@ -83,9 +90,40 @@ export default abstract class NymphDriver {
|
|
|
83
90
|
*/
|
|
84
91
|
abstract disconnect(): Promise<boolean>;
|
|
85
92
|
|
|
86
|
-
|
|
87
|
-
|
|
93
|
+
abstract needsMigration(): Promise<
|
|
94
|
+
'json' | 'tokens' | 'tilmeldColumns' | false
|
|
95
|
+
>;
|
|
96
|
+
abstract liveMigration(
|
|
97
|
+
migrationType: 'tokenTables' | 'tilmeldColumns' | 'tilmeldRemoveOldRows',
|
|
88
98
|
): Promise<void>;
|
|
99
|
+
|
|
100
|
+
abstract getEtypes(): Promise<string[]>;
|
|
101
|
+
abstract getIndexes(etype: string): Promise<
|
|
102
|
+
{
|
|
103
|
+
scope: 'data' | 'references' | 'tokens';
|
|
104
|
+
name: string;
|
|
105
|
+
property: string;
|
|
106
|
+
}[]
|
|
107
|
+
>;
|
|
108
|
+
abstract addIndex(
|
|
109
|
+
etype: string,
|
|
110
|
+
definition: {
|
|
111
|
+
scope: 'data' | 'references' | 'tokens';
|
|
112
|
+
name: string;
|
|
113
|
+
property: string;
|
|
114
|
+
},
|
|
115
|
+
): Promise<boolean>;
|
|
116
|
+
abstract deleteIndex(
|
|
117
|
+
etype: string,
|
|
118
|
+
scope: 'data' | 'references' | 'tokens',
|
|
119
|
+
name: string,
|
|
120
|
+
): Promise<boolean>;
|
|
121
|
+
|
|
122
|
+
abstract exportDataIterator(): AsyncGenerator<
|
|
123
|
+
{ type: 'comment' | 'uid' | 'entity'; content: string },
|
|
124
|
+
void,
|
|
125
|
+
undefined | false
|
|
126
|
+
>;
|
|
89
127
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
90
128
|
options: Options<T> & { return: 'count' },
|
|
91
129
|
...selectors: Selector[]
|
|
@@ -95,39 +133,45 @@ export default abstract class NymphDriver {
|
|
|
95
133
|
...selectors: Selector[]
|
|
96
134
|
): Promise<string[]>;
|
|
97
135
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
98
|
-
options
|
|
136
|
+
options: Options<T> & { return: 'object' },
|
|
99
137
|
...selectors: Selector[]
|
|
100
|
-
): Promise<
|
|
138
|
+
): Promise<EntityObjectType<T>[]>;
|
|
101
139
|
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
102
140
|
options?: Options<T>,
|
|
103
141
|
...selectors: Selector[]
|
|
104
|
-
): Promise<
|
|
105
|
-
|
|
106
|
-
T extends EntityConstructor = EntityConstructor
|
|
107
|
-
>(
|
|
108
|
-
options: Options<T> & { return: 'count' },
|
|
109
|
-
...selectors: Selector[]
|
|
110
|
-
): number;
|
|
111
|
-
protected abstract getEntitiesSync<
|
|
112
|
-
T extends EntityConstructor = EntityConstructor
|
|
113
|
-
>(
|
|
114
|
-
options: Options<T> & { return: 'guid' },
|
|
115
|
-
...selectors: Selector[]
|
|
116
|
-
): string[];
|
|
117
|
-
protected abstract getEntitiesSync<
|
|
118
|
-
T extends EntityConstructor = EntityConstructor
|
|
119
|
-
>(
|
|
120
|
-
options?: Options<T>,
|
|
121
|
-
...selectors: Selector[]
|
|
122
|
-
): ReturnType<T['factorySync']>[];
|
|
123
|
-
protected abstract getEntitiesSync<
|
|
124
|
-
T extends EntityConstructor = EntityConstructor
|
|
125
|
-
>(
|
|
142
|
+
): Promise<EntityInstanceType<T>[]>;
|
|
143
|
+
abstract getEntities<T extends EntityConstructor = EntityConstructor>(
|
|
126
144
|
options?: Options<T>,
|
|
127
145
|
...selectors: Selector[]
|
|
128
|
-
):
|
|
146
|
+
): Promise<
|
|
147
|
+
EntityInstanceType<T>[] | EntityObjectType<T>[] | string[] | number
|
|
148
|
+
>;
|
|
129
149
|
abstract getUID(name: string): Promise<number | null>;
|
|
130
|
-
abstract
|
|
150
|
+
abstract importEntity(entity: {
|
|
151
|
+
guid: string;
|
|
152
|
+
cdate: number;
|
|
153
|
+
mdate: number;
|
|
154
|
+
tags: string[];
|
|
155
|
+
sdata: SerializedEntityData;
|
|
156
|
+
etype: string;
|
|
157
|
+
}): Promise<void>;
|
|
158
|
+
abstract importEntityTokens(entity: {
|
|
159
|
+
guid: string;
|
|
160
|
+
cdate: number;
|
|
161
|
+
mdate: number;
|
|
162
|
+
tags: string[];
|
|
163
|
+
sdata: SerializedEntityData;
|
|
164
|
+
etype: string;
|
|
165
|
+
}): Promise<void>;
|
|
166
|
+
abstract importEntityTilmeldAC(entity: {
|
|
167
|
+
guid: string;
|
|
168
|
+
cdate: number;
|
|
169
|
+
mdate: number;
|
|
170
|
+
tags: string[];
|
|
171
|
+
sdata: SerializedEntityData;
|
|
172
|
+
etype: string;
|
|
173
|
+
}): Promise<void>;
|
|
174
|
+
abstract importUID(uid: { name: string; value: number }): Promise<void>;
|
|
131
175
|
abstract newUID(name: string): Promise<number | null>;
|
|
132
176
|
abstract renameUID(oldName: string, newName: string): Promise<boolean>;
|
|
133
177
|
abstract saveEntity(entity: EntityInterface): Promise<boolean>;
|
|
@@ -147,7 +191,7 @@ export default abstract class NymphDriver {
|
|
|
147
191
|
protected posixRegexMatch(
|
|
148
192
|
pattern: string,
|
|
149
193
|
subject: string,
|
|
150
|
-
caseInsensitive = false
|
|
194
|
+
caseInsensitive = false,
|
|
151
195
|
) {
|
|
152
196
|
const posixClasses = [
|
|
153
197
|
// '[[:<:]]',
|
|
@@ -198,15 +242,23 @@ export default abstract class NymphDriver {
|
|
|
198
242
|
return re.test(subject);
|
|
199
243
|
}
|
|
200
244
|
|
|
245
|
+
public checkIndexName(name: string) {
|
|
246
|
+
if (name.match(/[^a-z0-9_]/)) {
|
|
247
|
+
throw new Error(
|
|
248
|
+
'Index names can only contain lowercase letters, numbers, and underscores.',
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
201
253
|
public async export(filename: string) {
|
|
202
254
|
try {
|
|
203
255
|
const fhandle = fs.openSync(filename, 'w');
|
|
204
256
|
if (!fhandle) {
|
|
205
257
|
throw new InvalidParametersError('Provided filename is not writeable.');
|
|
206
258
|
}
|
|
207
|
-
await this.
|
|
208
|
-
fs.writeSync(fhandle,
|
|
209
|
-
}
|
|
259
|
+
for await (let entry of this.exportDataIterator()) {
|
|
260
|
+
fs.writeSync(fhandle, entry.content);
|
|
261
|
+
}
|
|
210
262
|
fs.closeSync(fhandle);
|
|
211
263
|
} catch (e: any) {
|
|
212
264
|
return false;
|
|
@@ -215,130 +267,157 @@ export default abstract class NymphDriver {
|
|
|
215
267
|
}
|
|
216
268
|
|
|
217
269
|
public async exportPrint() {
|
|
218
|
-
await this.
|
|
219
|
-
console.log(
|
|
220
|
-
}
|
|
270
|
+
for await (let entry of this.exportDataIterator()) {
|
|
271
|
+
console.log(entry.content);
|
|
272
|
+
}
|
|
221
273
|
return true;
|
|
222
274
|
}
|
|
223
275
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
guid: string,
|
|
228
|
-
tags: string[],
|
|
229
|
-
sdata: SerializedEntityData,
|
|
230
|
-
etype: string
|
|
231
|
-
) => Promise<void>,
|
|
232
|
-
saveUIDCallback: (name: string, value: number) => Promise<void>,
|
|
233
|
-
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
234
|
-
commitTransactionCallback: (() => Promise<void>) | null = null
|
|
276
|
+
public async importDataIterator(
|
|
277
|
+
lines: Iterable<string>,
|
|
278
|
+
transaction?: boolean,
|
|
235
279
|
) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
throw new InvalidParametersError('Provided filename is unreadable.');
|
|
280
|
+
const first = lines[Symbol.iterator]().next();
|
|
281
|
+
|
|
282
|
+
if (first.done || first.value !== '#nex2') {
|
|
283
|
+
throw new Error('Tried to import a file that is not a NEX v2 file.');
|
|
241
284
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
let tags: string[] = [];
|
|
246
|
-
let etype = '__undefined';
|
|
247
|
-
if (startTransactionCallback) {
|
|
248
|
-
await startTransactionCallback();
|
|
285
|
+
|
|
286
|
+
if (transaction) {
|
|
287
|
+
await this.internalTransaction('nymph-import');
|
|
249
288
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
/^\s
|
|
257
|
-
|
|
258
|
-
const propMatch = text.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
259
|
-
const uidMatch = text.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
260
|
-
if (uidMatch) {
|
|
261
|
-
// Add the UID.
|
|
262
|
-
await saveUIDCallback(uidMatch[1], Number(uidMatch[2]));
|
|
263
|
-
} else if (entityMatch) {
|
|
264
|
-
// Save the current entity.
|
|
265
|
-
if (guid) {
|
|
266
|
-
await saveEntityCallback(guid, tags, sdata, etype);
|
|
267
|
-
guid = null;
|
|
268
|
-
tags = [];
|
|
269
|
-
sdata = {};
|
|
270
|
-
etype = '__undefined';
|
|
289
|
+
try {
|
|
290
|
+
let guid: string | null = null;
|
|
291
|
+
let sdata: SerializedEntityData = {};
|
|
292
|
+
let tags: string[] = [];
|
|
293
|
+
let etype = '__undefined';
|
|
294
|
+
for (let line of lines) {
|
|
295
|
+
if (line.match(/^\s*#/)) {
|
|
296
|
+
continue;
|
|
271
297
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
298
|
+
const entityMatch = line.match(
|
|
299
|
+
/^\s*{([0-9A-Fa-f]+)}<([-\w_]+)>\[([^\]]*)\]\s*$/,
|
|
300
|
+
);
|
|
301
|
+
const propMatch = line.match(/^\s*([^=]+)\s*=\s*(.*\S)\s*$/);
|
|
302
|
+
const uidMatch = line.match(/^\s*<([^>]+)>\[(\d+)\]\s*$/);
|
|
303
|
+
if (uidMatch) {
|
|
304
|
+
// Add the UID.
|
|
305
|
+
await this.importUID({
|
|
306
|
+
name: uidMatch[1],
|
|
307
|
+
value: Number(uidMatch[2]),
|
|
308
|
+
});
|
|
309
|
+
} else if (entityMatch) {
|
|
310
|
+
// Save the current entity.
|
|
311
|
+
if (guid) {
|
|
312
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
313
|
+
delete sdata.cdate;
|
|
314
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
315
|
+
delete sdata.mdate;
|
|
316
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
317
|
+
guid = null;
|
|
318
|
+
tags = [];
|
|
319
|
+
sdata = {};
|
|
320
|
+
etype = '__undefined';
|
|
321
|
+
}
|
|
322
|
+
// Record the new entity's info.
|
|
323
|
+
guid = entityMatch[1];
|
|
324
|
+
etype = entityMatch[2];
|
|
325
|
+
tags = entityMatch[3].split(',');
|
|
326
|
+
} else if (propMatch) {
|
|
327
|
+
// Add the variable to the new entity.
|
|
328
|
+
if (guid) {
|
|
329
|
+
sdata[propMatch[1]] = propMatch[2];
|
|
330
|
+
}
|
|
280
331
|
}
|
|
332
|
+
// Clear the entity cache.
|
|
333
|
+
this.entityCache = {};
|
|
281
334
|
}
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
335
|
+
// Save the last entity.
|
|
336
|
+
if (guid) {
|
|
337
|
+
const cdate = Number(JSON.parse(sdata.cdate));
|
|
338
|
+
delete sdata.cdate;
|
|
339
|
+
const mdate = Number(JSON.parse(sdata.mdate));
|
|
340
|
+
delete sdata.mdate;
|
|
341
|
+
await this.importEntity({ guid, cdate, mdate, tags, sdata, etype });
|
|
342
|
+
}
|
|
343
|
+
if (transaction) {
|
|
344
|
+
await this.commit('nymph-import');
|
|
345
|
+
}
|
|
346
|
+
} catch (e: any) {
|
|
347
|
+
await this.rollback('nymph-import');
|
|
348
|
+
throw e;
|
|
291
349
|
}
|
|
292
350
|
return true;
|
|
293
351
|
}
|
|
294
352
|
|
|
353
|
+
public async importData(text: string, transaction?: boolean) {
|
|
354
|
+
return await this.importDataIterator(text.split('\n'), transaction);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
public async import(filename: string, transaction?: boolean) {
|
|
358
|
+
let rl: ReadLines;
|
|
359
|
+
try {
|
|
360
|
+
rl = new ReadLines(filename);
|
|
361
|
+
} catch (e: any) {
|
|
362
|
+
throw new InvalidParametersError('Provided filename is unreadable.');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const lines = {
|
|
366
|
+
*[Symbol.iterator]() {
|
|
367
|
+
let line: null | Buffer;
|
|
368
|
+
while ((line = rl.next())) {
|
|
369
|
+
yield line.toString('utf8');
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
return await this.importDataIterator(lines, transaction);
|
|
375
|
+
}
|
|
376
|
+
|
|
295
377
|
public checkData(
|
|
378
|
+
etype: string,
|
|
296
379
|
data: EntityData,
|
|
297
380
|
sdata: SerializedEntityData,
|
|
298
381
|
selectors: Selector[],
|
|
299
382
|
guid: string | null = null,
|
|
300
|
-
tags: string[] | null = null
|
|
383
|
+
tags: string[] | null = null,
|
|
301
384
|
) {
|
|
302
|
-
|
|
385
|
+
try {
|
|
386
|
+
const EntityClass = this.nymph.getEntityClassByEtype(etype);
|
|
387
|
+
const formattedSelectors = this.formatSelectors(selectors).selectors;
|
|
303
388
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
389
|
+
for (const curSelector of formattedSelectors) {
|
|
390
|
+
const type = curSelector.type;
|
|
391
|
+
const typeIsNot = type === '!&' || type === '!|';
|
|
392
|
+
const typeIsOr = type === '|' || type === '!|';
|
|
393
|
+
let pass = !typeIsOr;
|
|
394
|
+
for (const k in curSelector) {
|
|
395
|
+
const key = k as keyof Selector;
|
|
396
|
+
const value = curSelector[key];
|
|
312
397
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
398
|
+
if (key === 'type' || value == null) {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
316
401
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
xor(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
throw new Error("Can't use checkData on qref clauses.");
|
|
402
|
+
const clauseNot = key.substring(0, 1) === '!';
|
|
403
|
+
if (key === 'selector' || key === '!selector') {
|
|
404
|
+
const tmpArr = (
|
|
405
|
+
Array.isArray(value) ? value : [value]
|
|
406
|
+
) as Selector[];
|
|
407
|
+
pass = xor(
|
|
408
|
+
this.checkData(etype, data, sdata, tmpArr, guid, tags),
|
|
409
|
+
xor(typeIsNot, clauseNot),
|
|
410
|
+
);
|
|
327
411
|
} else {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if
|
|
332
|
-
|
|
333
|
-
|
|
412
|
+
if (key === 'qref' || key === '!qref') {
|
|
413
|
+
throw new Error("Can't use checkData on qref clauses.");
|
|
414
|
+
} else {
|
|
415
|
+
// Check if it doesn't pass any for &, check if it passes any for |.
|
|
416
|
+
for (const curValue of value) {
|
|
417
|
+
if (curValue == null) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
334
420
|
|
|
335
|
-
if (
|
|
336
|
-
((key === 'guid' || key === '!guid') && guid == null) ||
|
|
337
|
-
((key === 'tag' || key === '!tag') && tags == null)
|
|
338
|
-
) {
|
|
339
|
-
// Skip because it has already been checked (by the query).
|
|
340
|
-
pass = true;
|
|
341
|
-
} else {
|
|
342
421
|
const propName = (curValue as string[])[0];
|
|
343
422
|
|
|
344
423
|
// Unserialize the data for this variable.
|
|
@@ -356,7 +435,7 @@ export default abstract class NymphDriver {
|
|
|
356
435
|
case '!tag':
|
|
357
436
|
pass = xor(
|
|
358
437
|
(tags ?? []).indexOf(propName) !== -1,
|
|
359
|
-
xor(typeIsNot, clauseNot)
|
|
438
|
+
xor(typeIsNot, clauseNot),
|
|
360
439
|
);
|
|
361
440
|
break;
|
|
362
441
|
case 'defined':
|
|
@@ -367,7 +446,7 @@ export default abstract class NymphDriver {
|
|
|
367
446
|
case '!truthy':
|
|
368
447
|
pass = xor(
|
|
369
448
|
propName in data && data[propName],
|
|
370
|
-
xor(typeIsNot, clauseNot)
|
|
449
|
+
xor(typeIsNot, clauseNot),
|
|
371
450
|
);
|
|
372
451
|
break;
|
|
373
452
|
case 'ref':
|
|
@@ -377,9 +456,9 @@ export default abstract class NymphDriver {
|
|
|
377
456
|
propName in data &&
|
|
378
457
|
this.entityReferenceSearch(
|
|
379
458
|
data[propName],
|
|
380
|
-
testRefValue
|
|
459
|
+
testRefValue,
|
|
381
460
|
),
|
|
382
|
-
xor(typeIsNot, clauseNot)
|
|
461
|
+
xor(typeIsNot, clauseNot),
|
|
383
462
|
);
|
|
384
463
|
break;
|
|
385
464
|
case 'equal':
|
|
@@ -391,7 +470,7 @@ export default abstract class NymphDriver {
|
|
|
391
470
|
propName in data &&
|
|
392
471
|
JSON.stringify(data[propName]) ===
|
|
393
472
|
JSON.stringify(testEqualValue),
|
|
394
|
-
xor(typeIsNot, clauseNot)
|
|
473
|
+
xor(typeIsNot, clauseNot),
|
|
395
474
|
);
|
|
396
475
|
break;
|
|
397
476
|
case 'contain':
|
|
@@ -402,9 +481,9 @@ export default abstract class NymphDriver {
|
|
|
402
481
|
pass = xor(
|
|
403
482
|
propName in data &&
|
|
404
483
|
JSON.stringify(data[propName]).indexOf(
|
|
405
|
-
JSON.stringify(testContainValue)
|
|
484
|
+
JSON.stringify(testContainValue),
|
|
406
485
|
) !== -1,
|
|
407
|
-
xor(typeIsNot, clauseNot)
|
|
486
|
+
xor(typeIsNot, clauseNot),
|
|
408
487
|
);
|
|
409
488
|
break;
|
|
410
489
|
case 'like':
|
|
@@ -419,9 +498,27 @@ export default abstract class NymphDriver {
|
|
|
419
498
|
escapeRegExp(testLikeValue)
|
|
420
499
|
.replace('%', () => '.*')
|
|
421
500
|
.replace('_', () => '.') +
|
|
422
|
-
'$'
|
|
501
|
+
'$',
|
|
502
|
+
).test(data[propName]),
|
|
503
|
+
xor(typeIsNot, clauseNot),
|
|
504
|
+
);
|
|
505
|
+
break;
|
|
506
|
+
case 'ilike':
|
|
507
|
+
case '!ilike':
|
|
508
|
+
const testILikeValue = (
|
|
509
|
+
curValue as [string, string]
|
|
510
|
+
)[1] as string;
|
|
511
|
+
pass = xor(
|
|
512
|
+
propName in data &&
|
|
513
|
+
new RegExp(
|
|
514
|
+
'^' +
|
|
515
|
+
escapeRegExp(testILikeValue)
|
|
516
|
+
.replace('%', () => '.*')
|
|
517
|
+
.replace('_', () => '.') +
|
|
518
|
+
'$',
|
|
519
|
+
'i',
|
|
423
520
|
).test(data[propName]),
|
|
424
|
-
xor(typeIsNot, clauseNot)
|
|
521
|
+
xor(typeIsNot, clauseNot),
|
|
425
522
|
);
|
|
426
523
|
break;
|
|
427
524
|
case 'match':
|
|
@@ -433,7 +530,7 @@ export default abstract class NymphDriver {
|
|
|
433
530
|
pass = xor(
|
|
434
531
|
propName in data &&
|
|
435
532
|
this.posixRegexMatch(testMatchValue, data[propName]),
|
|
436
|
-
xor(typeIsNot, clauseNot)
|
|
533
|
+
xor(typeIsNot, clauseNot),
|
|
437
534
|
);
|
|
438
535
|
break;
|
|
439
536
|
case 'imatch':
|
|
@@ -447,9 +544,28 @@ export default abstract class NymphDriver {
|
|
|
447
544
|
this.posixRegexMatch(
|
|
448
545
|
testIMatchValue,
|
|
449
546
|
data[propName],
|
|
450
|
-
true
|
|
547
|
+
true,
|
|
451
548
|
),
|
|
452
|
-
xor(typeIsNot, clauseNot)
|
|
549
|
+
xor(typeIsNot, clauseNot),
|
|
550
|
+
);
|
|
551
|
+
break;
|
|
552
|
+
case 'search':
|
|
553
|
+
case '!search':
|
|
554
|
+
const testSearchValue = (
|
|
555
|
+
curValue as [string, string]
|
|
556
|
+
)[1] as string;
|
|
557
|
+
const tokenString =
|
|
558
|
+
propName in data
|
|
559
|
+
? (EntityClass.getFTSText(propName, data[propName]) ??
|
|
560
|
+
'')
|
|
561
|
+
: '';
|
|
562
|
+
pass = xor(
|
|
563
|
+
propName in data &&
|
|
564
|
+
this.tokenizer.searchString(
|
|
565
|
+
testSearchValue,
|
|
566
|
+
tokenString,
|
|
567
|
+
),
|
|
568
|
+
xor(typeIsNot, clauseNot),
|
|
453
569
|
);
|
|
454
570
|
break;
|
|
455
571
|
case 'gt':
|
|
@@ -459,7 +575,7 @@ export default abstract class NymphDriver {
|
|
|
459
575
|
)[1] as number;
|
|
460
576
|
pass = xor(
|
|
461
577
|
propName in data && data[propName] > testGTValue,
|
|
462
|
-
xor(typeIsNot, clauseNot)
|
|
578
|
+
xor(typeIsNot, clauseNot),
|
|
463
579
|
);
|
|
464
580
|
break;
|
|
465
581
|
case 'gte':
|
|
@@ -469,7 +585,7 @@ export default abstract class NymphDriver {
|
|
|
469
585
|
)[1] as number;
|
|
470
586
|
pass = xor(
|
|
471
587
|
propName in data && data[propName] >= testGTEValue,
|
|
472
|
-
xor(typeIsNot, clauseNot)
|
|
588
|
+
xor(typeIsNot, clauseNot),
|
|
473
589
|
);
|
|
474
590
|
break;
|
|
475
591
|
case 'lt':
|
|
@@ -479,7 +595,7 @@ export default abstract class NymphDriver {
|
|
|
479
595
|
)[1] as number;
|
|
480
596
|
pass = xor(
|
|
481
597
|
propName in data && data[propName] < testLTValue,
|
|
482
|
-
xor(typeIsNot, clauseNot)
|
|
598
|
+
xor(typeIsNot, clauseNot),
|
|
483
599
|
);
|
|
484
600
|
break;
|
|
485
601
|
case 'lte':
|
|
@@ -489,26 +605,33 @@ export default abstract class NymphDriver {
|
|
|
489
605
|
)[1] as number;
|
|
490
606
|
pass = xor(
|
|
491
607
|
propName in data && data[propName] <= testLTEValue,
|
|
492
|
-
xor(typeIsNot, clauseNot)
|
|
608
|
+
xor(typeIsNot, clauseNot),
|
|
493
609
|
);
|
|
494
610
|
break;
|
|
495
611
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
612
|
+
|
|
613
|
+
if (!xor(typeIsOr, pass)) {
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
499
616
|
}
|
|
500
617
|
}
|
|
501
618
|
}
|
|
619
|
+
if (!xor(typeIsOr, pass)) {
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
502
622
|
}
|
|
503
|
-
if (!
|
|
504
|
-
|
|
623
|
+
if (!pass) {
|
|
624
|
+
return false;
|
|
505
625
|
}
|
|
506
626
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
627
|
+
return true;
|
|
628
|
+
} catch (e: any) {
|
|
629
|
+
this.nymph.config.debugError(
|
|
630
|
+
'nymph',
|
|
631
|
+
`Failed to check entity data: ${e}`,
|
|
632
|
+
);
|
|
633
|
+
throw e;
|
|
510
634
|
}
|
|
511
|
-
return true;
|
|
512
635
|
}
|
|
513
636
|
|
|
514
637
|
/**
|
|
@@ -547,7 +670,7 @@ export default abstract class NymphDriver {
|
|
|
547
670
|
| EntityInterface
|
|
548
671
|
| EntityReference
|
|
549
672
|
| string
|
|
550
|
-
| (EntityInterface | EntityReference | string)[]
|
|
673
|
+
| (EntityInterface | EntityReference | string)[],
|
|
551
674
|
) {
|
|
552
675
|
// Get the GUID, if the passed $entity is an object.
|
|
553
676
|
const guids: string[] = [];
|
|
@@ -578,7 +701,7 @@ export default abstract class NymphDriver {
|
|
|
578
701
|
return true;
|
|
579
702
|
}
|
|
580
703
|
// Search through arrays and objects looking for the reference.
|
|
581
|
-
if (Array.isArray(value) || typeof value === 'object') {
|
|
704
|
+
if (value != null && (Array.isArray(value) || typeof value === 'object')) {
|
|
582
705
|
for (const key in value) {
|
|
583
706
|
if (this.entityReferenceSearch(value[key], guids)) {
|
|
584
707
|
return true;
|
|
@@ -588,9 +711,220 @@ export default abstract class NymphDriver {
|
|
|
588
711
|
return false;
|
|
589
712
|
}
|
|
590
713
|
|
|
714
|
+
protected removeAndReturnACValues(
|
|
715
|
+
etype: string,
|
|
716
|
+
data: EntityData,
|
|
717
|
+
sdata: SerializedEntityData,
|
|
718
|
+
) {
|
|
719
|
+
let user: string | null = null;
|
|
720
|
+
let group: string | null = null;
|
|
721
|
+
let acUser: number | null = null;
|
|
722
|
+
let acGroup: number | null = null;
|
|
723
|
+
let acOther: number | null = null;
|
|
724
|
+
let acRead: string[] | null = null;
|
|
725
|
+
let acWrite: string[] | null = null;
|
|
726
|
+
let acFull: string[] | null = null;
|
|
727
|
+
|
|
728
|
+
if (this.nymph.tilmeld != null) {
|
|
729
|
+
// Since Tilmeld is installed, we need to fill the AC vars.
|
|
730
|
+
const userValue =
|
|
731
|
+
'user' in sdata
|
|
732
|
+
? JSON.parse(sdata.user)
|
|
733
|
+
: 'user' in data
|
|
734
|
+
? data.user
|
|
735
|
+
: null;
|
|
736
|
+
if (
|
|
737
|
+
Array.isArray(userValue) &&
|
|
738
|
+
userValue[0] === 'nymph_entity_reference' &&
|
|
739
|
+
userValue[2] === 'User' &&
|
|
740
|
+
typeof userValue[1] === 'string' &&
|
|
741
|
+
userValue[1].match(/^[0-9a-f]{24}$/i)
|
|
742
|
+
) {
|
|
743
|
+
user = userValue[1];
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const groupValue =
|
|
747
|
+
'group' in sdata
|
|
748
|
+
? JSON.parse(sdata.group)
|
|
749
|
+
: 'group' in data
|
|
750
|
+
? data.group
|
|
751
|
+
: null;
|
|
752
|
+
if (
|
|
753
|
+
Array.isArray(groupValue) &&
|
|
754
|
+
groupValue[0] === 'nymph_entity_reference' &&
|
|
755
|
+
groupValue[2] === 'Group' &&
|
|
756
|
+
typeof groupValue[1] === 'string' &&
|
|
757
|
+
groupValue[1].match(/^[0-9a-f]{24}$/i)
|
|
758
|
+
) {
|
|
759
|
+
group = groupValue[1];
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (etype !== 'tilmeld_user' && etype !== 'tilmeld_group') {
|
|
763
|
+
const acUserValue =
|
|
764
|
+
'acUser' in sdata
|
|
765
|
+
? JSON.parse(sdata.acUser)
|
|
766
|
+
: 'acUser' in data
|
|
767
|
+
? data.acUser
|
|
768
|
+
: null;
|
|
769
|
+
if (
|
|
770
|
+
typeof acUserValue === 'number' &&
|
|
771
|
+
!isNaN(acUserValue) &&
|
|
772
|
+
acUserValue >= 0
|
|
773
|
+
) {
|
|
774
|
+
acUser = acUserValue;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const acGroupValue =
|
|
778
|
+
'acGroup' in sdata
|
|
779
|
+
? JSON.parse(sdata.acGroup)
|
|
780
|
+
: 'acGroup' in data
|
|
781
|
+
? data.acGroup
|
|
782
|
+
: null;
|
|
783
|
+
if (
|
|
784
|
+
typeof acGroupValue === 'number' &&
|
|
785
|
+
!isNaN(acGroupValue) &&
|
|
786
|
+
acGroupValue >= 0
|
|
787
|
+
) {
|
|
788
|
+
acGroup = acGroupValue;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
const acOtherValue =
|
|
792
|
+
'acOther' in sdata
|
|
793
|
+
? JSON.parse(sdata.acOther)
|
|
794
|
+
: 'acOther' in data
|
|
795
|
+
? data.acOther
|
|
796
|
+
: null;
|
|
797
|
+
if (
|
|
798
|
+
typeof acOtherValue === 'number' &&
|
|
799
|
+
!isNaN(acOtherValue) &&
|
|
800
|
+
acOtherValue >= 0
|
|
801
|
+
) {
|
|
802
|
+
acOther = acOtherValue;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const acReadValue =
|
|
806
|
+
'acRead' in sdata
|
|
807
|
+
? JSON.parse(sdata.acRead)
|
|
808
|
+
: 'acRead' in data
|
|
809
|
+
? data.acRead
|
|
810
|
+
: null;
|
|
811
|
+
if (Array.isArray(acReadValue)) {
|
|
812
|
+
const acReadArray = [];
|
|
813
|
+
for (let acReadEntry of acReadValue) {
|
|
814
|
+
if (
|
|
815
|
+
typeof acReadEntry === 'string' &&
|
|
816
|
+
acReadEntry.match(/^[0-9a-f]{24}$/i)
|
|
817
|
+
) {
|
|
818
|
+
acReadArray.push(acReadEntry);
|
|
819
|
+
} else if (
|
|
820
|
+
Array.isArray(acReadEntry) &&
|
|
821
|
+
acReadEntry[0] === 'nymph_entity_reference' &&
|
|
822
|
+
(acReadEntry[2] === 'User' || acReadEntry[2] === 'Group') &&
|
|
823
|
+
typeof acReadEntry[1] === 'string' &&
|
|
824
|
+
acReadEntry[1].match(/^[0-9a-f]{24}$/i)
|
|
825
|
+
) {
|
|
826
|
+
acReadArray.push(acReadEntry[1]);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
if (acReadArray.length) {
|
|
830
|
+
acRead = acReadArray;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const acWriteValue =
|
|
835
|
+
'acWrite' in sdata
|
|
836
|
+
? JSON.parse(sdata.acWrite)
|
|
837
|
+
: 'acWrite' in data
|
|
838
|
+
? data.acWrite
|
|
839
|
+
: null;
|
|
840
|
+
if (Array.isArray(acWriteValue)) {
|
|
841
|
+
const acWriteArray = [];
|
|
842
|
+
for (let acWriteEntry of acWriteValue) {
|
|
843
|
+
if (
|
|
844
|
+
typeof acWriteEntry === 'string' &&
|
|
845
|
+
acWriteEntry.match(/^[0-9a-f]{24}$/i)
|
|
846
|
+
) {
|
|
847
|
+
acWriteArray.push(acWriteEntry);
|
|
848
|
+
} else if (
|
|
849
|
+
Array.isArray(acWriteEntry) &&
|
|
850
|
+
acWriteEntry[0] === 'nymph_entity_reference' &&
|
|
851
|
+
(acWriteEntry[2] === 'User' || acWriteEntry[2] === 'Group') &&
|
|
852
|
+
typeof acWriteEntry[1] === 'string' &&
|
|
853
|
+
acWriteEntry[1].match(/^[0-9a-f]{24}$/i)
|
|
854
|
+
) {
|
|
855
|
+
acWriteArray.push(acWriteEntry[1]);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (acWriteArray.length) {
|
|
859
|
+
acWrite = acWriteArray;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const acFullValue =
|
|
864
|
+
'acFull' in sdata
|
|
865
|
+
? JSON.parse(sdata.acFull)
|
|
866
|
+
: 'acFull' in data
|
|
867
|
+
? data.acFull
|
|
868
|
+
: null;
|
|
869
|
+
if (Array.isArray(acFullValue)) {
|
|
870
|
+
const acFullArray = [];
|
|
871
|
+
for (let acFullEntry of acFullValue) {
|
|
872
|
+
if (
|
|
873
|
+
typeof acFullEntry === 'string' &&
|
|
874
|
+
acFullEntry.match(/^[0-9a-f]{24}$/i)
|
|
875
|
+
) {
|
|
876
|
+
acFullArray.push(acFullEntry);
|
|
877
|
+
} else if (
|
|
878
|
+
Array.isArray(acFullEntry) &&
|
|
879
|
+
acFullEntry[0] === 'nymph_entity_reference' &&
|
|
880
|
+
(acFullEntry[2] === 'User' || acFullEntry[2] === 'Group') &&
|
|
881
|
+
typeof acFullEntry[1] === 'string' &&
|
|
882
|
+
acFullEntry[1].match(/^[0-9a-f]{24}$/i)
|
|
883
|
+
) {
|
|
884
|
+
acFullArray.push(acFullEntry[1]);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
if (acFullArray.length) {
|
|
888
|
+
acFull = acFullArray;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// Delete all of the data, so it doesn't go into the database as rows in
|
|
894
|
+
// the data tables.
|
|
895
|
+
delete sdata.user;
|
|
896
|
+
delete data.user;
|
|
897
|
+
delete sdata.group;
|
|
898
|
+
delete data.group;
|
|
899
|
+
delete sdata.acUser;
|
|
900
|
+
delete data.acUser;
|
|
901
|
+
delete sdata.acGroup;
|
|
902
|
+
delete data.acGroup;
|
|
903
|
+
delete sdata.acOther;
|
|
904
|
+
delete data.acOther;
|
|
905
|
+
delete sdata.acRead;
|
|
906
|
+
delete data.acRead;
|
|
907
|
+
delete sdata.acWrite;
|
|
908
|
+
delete data.acWrite;
|
|
909
|
+
delete sdata.acFull;
|
|
910
|
+
delete data.acFull;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
return {
|
|
914
|
+
user,
|
|
915
|
+
group,
|
|
916
|
+
acUser,
|
|
917
|
+
acGroup,
|
|
918
|
+
acOther,
|
|
919
|
+
acRead,
|
|
920
|
+
acWrite,
|
|
921
|
+
acFull,
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
|
|
591
925
|
public formatSelectors(
|
|
592
926
|
selectors: Selector[],
|
|
593
|
-
options: Options = {}
|
|
927
|
+
options: Options = {},
|
|
594
928
|
): {
|
|
595
929
|
selectors: FormattedSelector[];
|
|
596
930
|
qrefs: [Options, ...FormattedSelector[]][];
|
|
@@ -625,10 +959,9 @@ export default abstract class NymphDriver {
|
|
|
625
959
|
for (let i = 0; i < tmpArr.length; i++) {
|
|
626
960
|
const name = tmpArr[i][0];
|
|
627
961
|
const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
|
|
628
|
-
const QrefEntityClass =
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
: qrefOptions.class ?? this.nymph.getEntityClass('Entity');
|
|
962
|
+
const QrefEntityClass = qrefOptions.class
|
|
963
|
+
? this.nymph.getEntityClass(qrefOptions.class)
|
|
964
|
+
: this.nymph.getEntityClass('Entity');
|
|
632
965
|
const newOptions = {
|
|
633
966
|
...qrefOptions,
|
|
634
967
|
class: QrefEntityClass,
|
|
@@ -637,7 +970,7 @@ export default abstract class NymphDriver {
|
|
|
637
970
|
const newSelectors = this.formatSelectors(qrefSelectors, options);
|
|
638
971
|
qrefs.push(
|
|
639
972
|
[newOptions, ...newSelectors.selectors],
|
|
640
|
-
...newSelectors.qrefs
|
|
973
|
+
...newSelectors.qrefs,
|
|
641
974
|
);
|
|
642
975
|
formatArr[i] = [name, [newOptions, ...newSelectors.selectors]];
|
|
643
976
|
}
|
|
@@ -685,12 +1018,12 @@ export default abstract class NymphDriver {
|
|
|
685
1018
|
|
|
686
1019
|
protected iterateSelectorsForQuery(
|
|
687
1020
|
selectors: FormattedSelector[],
|
|
688
|
-
callback: (
|
|
689
|
-
key: string
|
|
690
|
-
value: any
|
|
691
|
-
typeIsOr: boolean
|
|
692
|
-
typeIsNot: boolean
|
|
693
|
-
) => string
|
|
1021
|
+
callback: (data: {
|
|
1022
|
+
key: string;
|
|
1023
|
+
value: any;
|
|
1024
|
+
typeIsOr: boolean;
|
|
1025
|
+
typeIsNot: boolean;
|
|
1026
|
+
}) => string,
|
|
694
1027
|
) {
|
|
695
1028
|
const queryParts = [];
|
|
696
1029
|
for (const curSelector of selectors) {
|
|
@@ -703,7 +1036,7 @@ export default abstract class NymphDriver {
|
|
|
703
1036
|
if (key === 'type') {
|
|
704
1037
|
continue;
|
|
705
1038
|
}
|
|
706
|
-
let curQuery = callback(key, value, typeIsOr, typeIsNot);
|
|
1039
|
+
let curQuery = callback({ key, value, typeIsOr, typeIsNot });
|
|
707
1040
|
if (curQuery) {
|
|
708
1041
|
if (curSelectorQuery) {
|
|
709
1042
|
curSelectorQuery += typeIsOr ? ' OR ' : ' AND ';
|
|
@@ -719,71 +1052,14 @@ export default abstract class NymphDriver {
|
|
|
719
1052
|
return queryParts;
|
|
720
1053
|
}
|
|
721
1054
|
|
|
722
|
-
|
|
723
|
-
* Get the first entity to match all options/selectors, synchronously.
|
|
724
|
-
*
|
|
725
|
-
* This should really only be used internally by the Entity class. It's a bad
|
|
726
|
-
* idea to get entities synchronously. The Entity class uses this to load
|
|
727
|
-
* sleeping references.
|
|
728
|
-
*
|
|
729
|
-
* @param options The options.
|
|
730
|
-
* @param selectors Unlimited optional selectors to search for, or a single GUID. If none are given, all entities are searched for the given options.
|
|
731
|
-
* @returns An entity, or null on failure or nothing found.
|
|
732
|
-
*/
|
|
733
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
734
|
-
options: Options<T> & { return: 'count' },
|
|
735
|
-
...selectors: Selector[]
|
|
736
|
-
): number;
|
|
737
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
738
|
-
options: Options<T> & { return: 'guid' },
|
|
739
|
-
...selectors: Selector[]
|
|
740
|
-
): string | null;
|
|
741
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
742
|
-
options: Options<T>,
|
|
743
|
-
...selectors: Selector[]
|
|
744
|
-
): ReturnType<T['factorySync']> | null;
|
|
745
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
746
|
-
options: Options<T> & { return: 'count' },
|
|
747
|
-
guid: string
|
|
748
|
-
): number;
|
|
749
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
750
|
-
options: Options<T> & { return: 'guid' },
|
|
751
|
-
guid: string
|
|
752
|
-
): string | null;
|
|
753
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
754
|
-
options: Options<T>,
|
|
755
|
-
guid: string
|
|
756
|
-
): ReturnType<T['factorySync']> | null;
|
|
757
|
-
public getEntitySync<T extends EntityConstructor = EntityConstructor>(
|
|
758
|
-
options: Options<T> = {},
|
|
759
|
-
...selectors: Selector[] | string[]
|
|
760
|
-
): ReturnType<T['factorySync']> | string | number | null {
|
|
761
|
-
// Set up options and selectors.
|
|
762
|
-
if (typeof selectors[0] === 'string') {
|
|
763
|
-
selectors = [{ type: '&', guid: selectors[0] }];
|
|
764
|
-
}
|
|
765
|
-
options.limit = 1;
|
|
766
|
-
const entities = this.getEntitiesSync(
|
|
767
|
-
options,
|
|
768
|
-
...(selectors as Selector[])
|
|
769
|
-
);
|
|
770
|
-
if (options.return === 'count') {
|
|
771
|
-
return entities as unknown as number;
|
|
772
|
-
}
|
|
773
|
-
if (!entities || !entities.length) {
|
|
774
|
-
return null;
|
|
775
|
-
}
|
|
776
|
-
return entities[0];
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
protected getEntitesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
1055
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
780
1056
|
options: Options<T> & { return: 'count' },
|
|
781
1057
|
selectors: Selector[],
|
|
782
|
-
performQueryCallback: (
|
|
783
|
-
options: Options<T
|
|
784
|
-
|
|
785
|
-
etype: string
|
|
786
|
-
) => {
|
|
1058
|
+
performQueryCallback: (data: {
|
|
1059
|
+
options: Options<T>;
|
|
1060
|
+
selectors: FormattedSelector[];
|
|
1061
|
+
etype: string;
|
|
1062
|
+
}) => {
|
|
787
1063
|
result: any;
|
|
788
1064
|
},
|
|
789
1065
|
rowFetchCallback: () => any,
|
|
@@ -798,16 +1074,16 @@ export default abstract class NymphDriver {
|
|
|
798
1074
|
getDataNameAndSValueCallback: (row: any) => {
|
|
799
1075
|
name: string;
|
|
800
1076
|
svalue: string;
|
|
801
|
-
}
|
|
1077
|
+
},
|
|
802
1078
|
): { result: any; process: () => number | Error };
|
|
803
|
-
protected
|
|
1079
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
804
1080
|
options: Options<T> & { return: 'guid' },
|
|
805
1081
|
selectors: Selector[],
|
|
806
|
-
performQueryCallback: (
|
|
807
|
-
options: Options<T
|
|
808
|
-
|
|
809
|
-
etype: string
|
|
810
|
-
) => {
|
|
1082
|
+
performQueryCallback: (data: {
|
|
1083
|
+
options: Options<T>;
|
|
1084
|
+
selectors: FormattedSelector[];
|
|
1085
|
+
etype: string;
|
|
1086
|
+
}) => {
|
|
811
1087
|
result: any;
|
|
812
1088
|
},
|
|
813
1089
|
rowFetchCallback: () => any,
|
|
@@ -822,16 +1098,40 @@ export default abstract class NymphDriver {
|
|
|
822
1098
|
getDataNameAndSValueCallback: (row: any) => {
|
|
823
1099
|
name: string;
|
|
824
1100
|
svalue: string;
|
|
825
|
-
}
|
|
1101
|
+
},
|
|
826
1102
|
): { result: any; process: () => string[] | Error };
|
|
827
|
-
protected
|
|
1103
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
1104
|
+
options: Options<T> & { return: 'object' },
|
|
1105
|
+
selectors: Selector[],
|
|
1106
|
+
performQueryCallback: (data: {
|
|
1107
|
+
options: Options<T>;
|
|
1108
|
+
selectors: FormattedSelector[];
|
|
1109
|
+
etype: string;
|
|
1110
|
+
}) => {
|
|
1111
|
+
result: any;
|
|
1112
|
+
},
|
|
1113
|
+
rowFetchCallback: () => any,
|
|
1114
|
+
freeResultCallback: () => void,
|
|
1115
|
+
getCountCallback: (row: any) => number,
|
|
1116
|
+
getGUIDCallback: (row: any) => string,
|
|
1117
|
+
getTagsAndDatesCallback: (row: any) => {
|
|
1118
|
+
tags: string[];
|
|
1119
|
+
cdate: number;
|
|
1120
|
+
mdate: number;
|
|
1121
|
+
},
|
|
1122
|
+
getDataNameAndSValueCallback: (row: any) => {
|
|
1123
|
+
name: string;
|
|
1124
|
+
svalue: string;
|
|
1125
|
+
},
|
|
1126
|
+
): { result: any; process: () => EntityObjectType<T>[] | Error };
|
|
1127
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
828
1128
|
options: Options<T>,
|
|
829
1129
|
selectors: Selector[],
|
|
830
|
-
performQueryCallback: (
|
|
831
|
-
options: Options<T
|
|
832
|
-
|
|
833
|
-
etype: string
|
|
834
|
-
) => {
|
|
1130
|
+
performQueryCallback: (data: {
|
|
1131
|
+
options: Options<T>;
|
|
1132
|
+
selectors: FormattedSelector[];
|
|
1133
|
+
etype: string;
|
|
1134
|
+
}) => {
|
|
835
1135
|
result: any;
|
|
836
1136
|
},
|
|
837
1137
|
rowFetchCallback: () => any,
|
|
@@ -846,16 +1146,19 @@ export default abstract class NymphDriver {
|
|
|
846
1146
|
getDataNameAndSValueCallback: (row: any) => {
|
|
847
1147
|
name: string;
|
|
848
1148
|
svalue: string;
|
|
849
|
-
}
|
|
850
|
-
): {
|
|
851
|
-
|
|
1149
|
+
},
|
|
1150
|
+
): {
|
|
1151
|
+
result: any;
|
|
1152
|
+
process: () => EntityInstanceType<T>[] | Error;
|
|
1153
|
+
};
|
|
1154
|
+
protected getEntitiesRowLike<T extends EntityConstructor = EntityConstructor>(
|
|
852
1155
|
options: Options<T>,
|
|
853
1156
|
selectors: Selector[],
|
|
854
|
-
performQueryCallback: (
|
|
855
|
-
options: Options<T
|
|
856
|
-
|
|
857
|
-
etype: string
|
|
858
|
-
) => {
|
|
1157
|
+
performQueryCallback: (data: {
|
|
1158
|
+
options: Options<T>;
|
|
1159
|
+
selectors: FormattedSelector[];
|
|
1160
|
+
etype: string;
|
|
1161
|
+
}) => {
|
|
859
1162
|
result: any;
|
|
860
1163
|
},
|
|
861
1164
|
rowFetchCallback: () => any,
|
|
@@ -866,14 +1169,27 @@ export default abstract class NymphDriver {
|
|
|
866
1169
|
tags: string[];
|
|
867
1170
|
cdate: number;
|
|
868
1171
|
mdate: number;
|
|
1172
|
+
user?: string;
|
|
1173
|
+
group?: string;
|
|
1174
|
+
acUser?: number;
|
|
1175
|
+
acGroup?: number;
|
|
1176
|
+
acOther?: number;
|
|
1177
|
+
acRead?: string[];
|
|
1178
|
+
acWrite?: string[];
|
|
1179
|
+
acFull?: string[];
|
|
869
1180
|
},
|
|
870
1181
|
getDataNameAndSValueCallback: (row: any) => {
|
|
871
1182
|
name: string;
|
|
872
1183
|
svalue: string;
|
|
873
|
-
}
|
|
1184
|
+
},
|
|
874
1185
|
): {
|
|
875
1186
|
result: any;
|
|
876
|
-
process: () =>
|
|
1187
|
+
process: () =>
|
|
1188
|
+
| EntityInstanceType<T>[]
|
|
1189
|
+
| EntityObjectType<T>[]
|
|
1190
|
+
| string[]
|
|
1191
|
+
| number
|
|
1192
|
+
| Error;
|
|
877
1193
|
} {
|
|
878
1194
|
if (!this.isConnected()) {
|
|
879
1195
|
throw new UnableToConnectError('not connected to DB');
|
|
@@ -886,21 +1202,29 @@ export default abstract class NymphDriver {
|
|
|
886
1202
|
['&', '!&', '|', '!|'].indexOf(selector.type) === -1
|
|
887
1203
|
) {
|
|
888
1204
|
throw new InvalidParametersError(
|
|
889
|
-
'Invalid query selector passed: ' + JSON.stringify(selector)
|
|
1205
|
+
'Invalid query selector passed: ' + JSON.stringify(selector),
|
|
890
1206
|
);
|
|
891
1207
|
}
|
|
892
1208
|
}
|
|
893
1209
|
|
|
894
|
-
let entities:
|
|
1210
|
+
let entities: EntityInstanceType<T>[] | EntityObjectType<T>[] | string[] =
|
|
1211
|
+
[];
|
|
895
1212
|
let count = 0;
|
|
896
1213
|
const EntityClass =
|
|
897
1214
|
options.class ?? (this.nymph.getEntityClass('Entity') as T);
|
|
898
1215
|
const etype = EntityClass.ETYPE;
|
|
899
1216
|
|
|
1217
|
+
if (options.source === 'client' && options.return === 'object') {
|
|
1218
|
+
throw new InvalidParametersError(
|
|
1219
|
+
'Object return type not allowed from client.',
|
|
1220
|
+
);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
900
1223
|
// Check if the requested entity is cached.
|
|
901
1224
|
if (
|
|
902
1225
|
this.nymph.config.cache &&
|
|
903
1226
|
!options.skipCache &&
|
|
1227
|
+
selectors.length &&
|
|
904
1228
|
'guid' in selectors[0] &&
|
|
905
1229
|
typeof selectors[0].guid === 'number'
|
|
906
1230
|
) {
|
|
@@ -912,33 +1236,75 @@ export default abstract class NymphDriver {
|
|
|
912
1236
|
(Object.keys(selectors[0]).length === 2 ||
|
|
913
1237
|
(Object.keys(selectors[0]).length === 3 && 'tag' in selectors[0]))
|
|
914
1238
|
) {
|
|
915
|
-
const
|
|
916
|
-
|
|
917
|
-
EntityClass.class,
|
|
918
|
-
!!options.skipAc
|
|
919
|
-
);
|
|
1239
|
+
const cacheEntity = this.pullCache(selectors[0]['guid']);
|
|
1240
|
+
|
|
920
1241
|
if (
|
|
921
|
-
|
|
922
|
-
|
|
1242
|
+
cacheEntity != null &&
|
|
1243
|
+
cacheEntity.guid != null &&
|
|
923
1244
|
(!('tag' in selectors[0]) ||
|
|
924
|
-
|
|
925
|
-
|
|
1245
|
+
!(
|
|
1246
|
+
Array.isArray(selectors[0].tag)
|
|
926
1247
|
? selectors[0].tag
|
|
927
|
-
: [selectors[0].tag]
|
|
928
|
-
))
|
|
1248
|
+
: [selectors[0].tag]
|
|
1249
|
+
).find((tag) => tag == null || !cacheEntity.tags.includes(tag)))
|
|
929
1250
|
) {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1251
|
+
// Return the value from the cache.
|
|
1252
|
+
const { guid, cdate, mdate, tags, data, sdata } = cacheEntity;
|
|
1253
|
+
|
|
1254
|
+
if (options.return === 'count') {
|
|
1255
|
+
return {
|
|
1256
|
+
result: Promise.resolve(null),
|
|
1257
|
+
process: () => 1,
|
|
1258
|
+
};
|
|
1259
|
+
} else if (options.return === 'guid') {
|
|
1260
|
+
return {
|
|
1261
|
+
result: Promise.resolve(null),
|
|
1262
|
+
process: () => [guid],
|
|
1263
|
+
};
|
|
1264
|
+
} else if (options.return === 'object') {
|
|
1265
|
+
return {
|
|
1266
|
+
result: Promise.resolve(null),
|
|
1267
|
+
process: () => [
|
|
1268
|
+
{
|
|
1269
|
+
guid,
|
|
1270
|
+
cdate,
|
|
1271
|
+
mdate,
|
|
1272
|
+
tags,
|
|
1273
|
+
...data,
|
|
1274
|
+
...Object.fromEntries(
|
|
1275
|
+
Object.entries(sdata).map(([name, value]) => [
|
|
1276
|
+
name,
|
|
1277
|
+
JSON.parse(value),
|
|
1278
|
+
]),
|
|
1279
|
+
),
|
|
1280
|
+
} as EntityObjectType<T>,
|
|
1281
|
+
],
|
|
1282
|
+
};
|
|
1283
|
+
} else {
|
|
1284
|
+
const entity = this.nymph
|
|
1285
|
+
.getEntityClass(EntityClass.class)
|
|
1286
|
+
.factorySync() as EntityInstanceType<T>;
|
|
1287
|
+
if (entity) {
|
|
1288
|
+
entity.$nymph = this.nymph;
|
|
1289
|
+
if (options.skipAc != null) {
|
|
1290
|
+
entity.$useSkipAc(!!options.skipAc);
|
|
1291
|
+
}
|
|
1292
|
+
entity.guid = guid;
|
|
1293
|
+
entity.cdate = cdate;
|
|
1294
|
+
entity.mdate = mdate;
|
|
1295
|
+
entity.tags = tags;
|
|
1296
|
+
entity.$putData(data, sdata, 'server');
|
|
1297
|
+
return {
|
|
1298
|
+
result: Promise.resolve(null),
|
|
1299
|
+
process: () => [entity],
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
return {
|
|
1304
|
+
result: Promise.resolve(null),
|
|
1305
|
+
process: () => [],
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
942
1308
|
}
|
|
943
1309
|
}
|
|
944
1310
|
}
|
|
@@ -946,15 +1312,16 @@ export default abstract class NymphDriver {
|
|
|
946
1312
|
try {
|
|
947
1313
|
const formattedSelectors = this.formatSelectors(selectors, options);
|
|
948
1314
|
this.nymph.runQueryCallbacks(options, formattedSelectors.selectors);
|
|
949
|
-
for (let
|
|
950
|
-
const [options, ...selectors] =
|
|
1315
|
+
for (let i = 0; i < formattedSelectors.qrefs.length; i++) {
|
|
1316
|
+
const [options, ...selectors] = formattedSelectors.qrefs[i];
|
|
951
1317
|
this.nymph.runQueryCallbacks(options, selectors);
|
|
1318
|
+
formattedSelectors.qrefs[i] = [options, ...selectors];
|
|
952
1319
|
}
|
|
953
|
-
const { result } = performQueryCallback(
|
|
1320
|
+
const { result } = performQueryCallback({
|
|
954
1321
|
options,
|
|
955
|
-
formattedSelectors.selectors,
|
|
956
|
-
etype
|
|
957
|
-
);
|
|
1322
|
+
selectors: formattedSelectors.selectors,
|
|
1323
|
+
etype,
|
|
1324
|
+
});
|
|
958
1325
|
|
|
959
1326
|
return {
|
|
960
1327
|
result,
|
|
@@ -994,28 +1361,86 @@ export default abstract class NymphDriver {
|
|
|
994
1361
|
// Make sure that $row is incremented :)
|
|
995
1362
|
row = rowFetchCallback();
|
|
996
1363
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1364
|
+
|
|
1365
|
+
if (this.nymph.tilmeld) {
|
|
1366
|
+
if ('user' in tagsAndDates && tagsAndDates.user != null) {
|
|
1367
|
+
data.user = [
|
|
1368
|
+
'nymph_entity_reference',
|
|
1369
|
+
tagsAndDates.user,
|
|
1370
|
+
'User',
|
|
1371
|
+
];
|
|
1372
|
+
delete sdata.user;
|
|
1373
|
+
}
|
|
1374
|
+
if ('group' in tagsAndDates && tagsAndDates.group != null) {
|
|
1375
|
+
data.group = [
|
|
1376
|
+
'nymph_entity_reference',
|
|
1377
|
+
tagsAndDates.group,
|
|
1378
|
+
'Group',
|
|
1379
|
+
];
|
|
1380
|
+
delete sdata.group;
|
|
1381
|
+
}
|
|
1382
|
+
if ('acUser' in tagsAndDates && tagsAndDates.acUser != null) {
|
|
1383
|
+
data.acUser = tagsAndDates.acUser;
|
|
1384
|
+
delete sdata.acUser;
|
|
1385
|
+
}
|
|
1386
|
+
if ('acGroup' in tagsAndDates && tagsAndDates.acGroup != null) {
|
|
1387
|
+
data.acGroup = tagsAndDates.acGroup;
|
|
1388
|
+
delete sdata.acGroup;
|
|
1389
|
+
}
|
|
1390
|
+
if ('acOther' in tagsAndDates && tagsAndDates.acOther != null) {
|
|
1391
|
+
data.acOther = tagsAndDates.acOther;
|
|
1392
|
+
delete sdata.acOther;
|
|
1393
|
+
}
|
|
1394
|
+
if ('acRead' in tagsAndDates && tagsAndDates.acRead != null) {
|
|
1395
|
+
data.acRead = tagsAndDates.acRead;
|
|
1396
|
+
delete sdata.acRead;
|
|
1397
|
+
}
|
|
1398
|
+
if ('acWrite' in tagsAndDates && tagsAndDates.acWrite != null) {
|
|
1399
|
+
data.acWrite = tagsAndDates.acWrite;
|
|
1400
|
+
delete sdata.acWrite;
|
|
1401
|
+
}
|
|
1402
|
+
if ('acFull' in tagsAndDates && tagsAndDates.acFull != null) {
|
|
1403
|
+
data.acFull = tagsAndDates.acFull;
|
|
1404
|
+
delete sdata.acFull;
|
|
1405
|
+
}
|
|
1003
1406
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1407
|
+
|
|
1408
|
+
if (options.return === 'object') {
|
|
1409
|
+
(entities as EntityObjectType<T>[]).push({
|
|
1410
|
+
guid,
|
|
1411
|
+
cdate,
|
|
1412
|
+
mdate,
|
|
1413
|
+
tags,
|
|
1414
|
+
...data,
|
|
1415
|
+
...Object.fromEntries(
|
|
1416
|
+
Object.entries(sdata).map(([name, value]) => [
|
|
1417
|
+
name,
|
|
1418
|
+
JSON.parse(value),
|
|
1419
|
+
]),
|
|
1420
|
+
),
|
|
1421
|
+
} as EntityObjectType<T>);
|
|
1422
|
+
} else {
|
|
1423
|
+
const entity =
|
|
1424
|
+
EntityClass.factorySync() as EntityInstanceType<T>;
|
|
1425
|
+
entity.$nymph = this.nymph;
|
|
1426
|
+
if (options.skipAc != null) {
|
|
1427
|
+
entity.$useSkipAc(!!options.skipAc);
|
|
1428
|
+
}
|
|
1429
|
+
entity.guid = guid;
|
|
1430
|
+
entity.cdate = cdate;
|
|
1431
|
+
entity.mdate = mdate;
|
|
1432
|
+
entity.tags = tags;
|
|
1433
|
+
this.putDataCounter++;
|
|
1434
|
+
if (this.putDataCounter == 100) {
|
|
1435
|
+
throw new Error('Infinite loop detected in Entity loading.');
|
|
1436
|
+
}
|
|
1437
|
+
entity.$putData(data, sdata, 'server');
|
|
1438
|
+
this.putDataCounter--;
|
|
1439
|
+
entities.push(entity);
|
|
1011
1440
|
}
|
|
1012
|
-
entity.$putData(data, sdata);
|
|
1013
|
-
this.putDataCounter--;
|
|
1014
1441
|
if (this.nymph.config.cache) {
|
|
1015
1442
|
this.pushCache(guid, cdate, mdate, tags, data, sdata);
|
|
1016
1443
|
}
|
|
1017
|
-
// @ts-ignore: ts doesn't know the return type here.
|
|
1018
|
-
entities.push(entity);
|
|
1019
1444
|
}
|
|
1020
1445
|
}
|
|
1021
1446
|
|
|
@@ -1036,88 +1461,115 @@ export default abstract class NymphDriver {
|
|
|
1036
1461
|
|
|
1037
1462
|
protected async saveEntityRowLike(
|
|
1038
1463
|
entity: EntityInterface,
|
|
1039
|
-
saveNewEntityCallback: (
|
|
1040
|
-
entity: EntityInterface
|
|
1041
|
-
guid: string
|
|
1042
|
-
tags: string[]
|
|
1043
|
-
data: EntityData
|
|
1044
|
-
sdata: SerializedEntityData
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1464
|
+
saveNewEntityCallback: (data: {
|
|
1465
|
+
entity: EntityInterface;
|
|
1466
|
+
guid: string;
|
|
1467
|
+
tags: string[];
|
|
1468
|
+
data: EntityData;
|
|
1469
|
+
sdata: SerializedEntityData;
|
|
1470
|
+
uniques: string[];
|
|
1471
|
+
cdate: number;
|
|
1472
|
+
etype: string;
|
|
1473
|
+
}) => Promise<boolean>,
|
|
1474
|
+
saveExistingEntityCallback: (data: {
|
|
1475
|
+
entity: EntityInterface;
|
|
1476
|
+
guid: string;
|
|
1477
|
+
tags: string[];
|
|
1478
|
+
data: EntityData;
|
|
1479
|
+
sdata: SerializedEntityData;
|
|
1480
|
+
uniques: string[];
|
|
1481
|
+
mdate: number;
|
|
1482
|
+
etype: string;
|
|
1483
|
+
}) => Promise<boolean>,
|
|
1057
1484
|
startTransactionCallback: (() => Promise<void>) | null = null,
|
|
1058
1485
|
commitTransactionCallback:
|
|
1059
1486
|
| ((success: boolean) => Promise<boolean>)
|
|
1060
|
-
| null = null
|
|
1487
|
+
| null = null,
|
|
1061
1488
|
) {
|
|
1489
|
+
const originalGuid = entity.guid;
|
|
1490
|
+
const originalCdate = entity.cdate;
|
|
1491
|
+
const originalMdate = entity.mdate;
|
|
1062
1492
|
// Get a modified date.
|
|
1063
|
-
|
|
1493
|
+
let mdate = Date.now();
|
|
1494
|
+
if (entity.mdate != null) {
|
|
1495
|
+
if (this.nymph.config.updateMDate === false) {
|
|
1496
|
+
mdate = entity.mdate;
|
|
1497
|
+
} else if (typeof this.nymph.config.updateMDate === 'number') {
|
|
1498
|
+
mdate = entity.mdate + this.nymph.config.updateMDate;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1064
1501
|
const tags = difference(entity.tags, ['']);
|
|
1065
1502
|
const data = entity.$getData();
|
|
1066
1503
|
const sdata = entity.$getSData();
|
|
1504
|
+
const uniques = await entity.$getUniques();
|
|
1067
1505
|
const EntityClass = entity.constructor as EntityConstructor;
|
|
1068
1506
|
const etype = EntityClass.ETYPE;
|
|
1069
1507
|
if (startTransactionCallback) {
|
|
1070
1508
|
await startTransactionCallback();
|
|
1071
1509
|
}
|
|
1072
1510
|
let success = false;
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1511
|
+
try {
|
|
1512
|
+
if (entity.guid == null) {
|
|
1513
|
+
const cdate = mdate;
|
|
1514
|
+
const newId = entity.$getGuaranteedGUID();
|
|
1515
|
+
success = await saveNewEntityCallback({
|
|
1516
|
+
entity,
|
|
1517
|
+
guid: newId,
|
|
1518
|
+
tags,
|
|
1519
|
+
data,
|
|
1520
|
+
sdata,
|
|
1521
|
+
uniques,
|
|
1522
|
+
cdate,
|
|
1523
|
+
etype,
|
|
1524
|
+
});
|
|
1525
|
+
if (success) {
|
|
1526
|
+
entity.guid = newId;
|
|
1527
|
+
entity.cdate = cdate;
|
|
1528
|
+
entity.mdate = mdate;
|
|
1529
|
+
}
|
|
1530
|
+
} else {
|
|
1531
|
+
// Removed any cached versions of this entity.
|
|
1532
|
+
if (this.nymph.config.cache) {
|
|
1533
|
+
this.cleanCache(entity.guid);
|
|
1534
|
+
}
|
|
1535
|
+
success = await saveExistingEntityCallback({
|
|
1536
|
+
entity,
|
|
1537
|
+
guid: entity.guid,
|
|
1538
|
+
tags,
|
|
1539
|
+
data,
|
|
1540
|
+
sdata,
|
|
1541
|
+
uniques,
|
|
1542
|
+
mdate,
|
|
1543
|
+
etype,
|
|
1544
|
+
});
|
|
1545
|
+
if (success) {
|
|
1546
|
+
entity.mdate = mdate;
|
|
1547
|
+
}
|
|
1089
1548
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
if (this.nymph.config.cache) {
|
|
1093
|
-
this.cleanCache(entity.guid);
|
|
1549
|
+
if (commitTransactionCallback) {
|
|
1550
|
+
success = await commitTransactionCallback(success);
|
|
1094
1551
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
entity.mdate = mdate;
|
|
1552
|
+
// Cache the entity.
|
|
1553
|
+
if (success && this.nymph.config.cache) {
|
|
1554
|
+
this.pushCache(
|
|
1555
|
+
entity.guid as string,
|
|
1556
|
+
entity.cdate as number,
|
|
1557
|
+
entity.mdate as number,
|
|
1558
|
+
entity.tags,
|
|
1559
|
+
data,
|
|
1560
|
+
sdata,
|
|
1561
|
+
);
|
|
1106
1562
|
}
|
|
1563
|
+
} catch (e: any) {
|
|
1564
|
+
entity.guid = originalGuid;
|
|
1565
|
+
entity.cdate = originalCdate;
|
|
1566
|
+
entity.mdate = originalMdate;
|
|
1567
|
+
throw e;
|
|
1107
1568
|
}
|
|
1108
|
-
if (
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
if (success && this.nymph.config.cache) {
|
|
1113
|
-
this.pushCache(
|
|
1114
|
-
entity.guid as string,
|
|
1115
|
-
entity.cdate as number,
|
|
1116
|
-
entity.mdate as number,
|
|
1117
|
-
entity.tags,
|
|
1118
|
-
data,
|
|
1119
|
-
sdata
|
|
1120
|
-
);
|
|
1569
|
+
if (!success) {
|
|
1570
|
+
entity.guid = originalGuid;
|
|
1571
|
+
entity.cdate = originalCdate;
|
|
1572
|
+
entity.mdate = originalMdate;
|
|
1121
1573
|
}
|
|
1122
1574
|
return success;
|
|
1123
1575
|
}
|
|
@@ -1126,35 +1578,30 @@ export default abstract class NymphDriver {
|
|
|
1126
1578
|
* Pull an entity from the cache.
|
|
1127
1579
|
*
|
|
1128
1580
|
* @param guid The entity's GUID.
|
|
1129
|
-
* @
|
|
1130
|
-
* @param useSkipAc Whether to tell the entity to use skip_ac.
|
|
1131
|
-
* @returns The entity or null if it's not cached.
|
|
1581
|
+
* @returns The entity's data or null if it's not cached.
|
|
1132
1582
|
*/
|
|
1133
|
-
protected pullCache
|
|
1134
|
-
guid: string
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1583
|
+
protected pullCache(guid: string): {
|
|
1584
|
+
guid: string;
|
|
1585
|
+
cdate: number;
|
|
1586
|
+
mdate: number;
|
|
1587
|
+
tags: string[];
|
|
1588
|
+
data: EntityData;
|
|
1589
|
+
sdata: SerializedEntityData;
|
|
1590
|
+
} | null {
|
|
1138
1591
|
// Increment the entity access count.
|
|
1139
1592
|
if (!(guid in this.entityCount)) {
|
|
1140
1593
|
this.entityCount[guid] = 0;
|
|
1141
1594
|
}
|
|
1142
1595
|
this.entityCount[guid]++;
|
|
1143
1596
|
if (guid in this.entityCache) {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
entity.$putData(
|
|
1153
|
-
this.entityCache[guid]['data'],
|
|
1154
|
-
this.entityCache[guid]['sdata']
|
|
1155
|
-
);
|
|
1156
|
-
return entity;
|
|
1157
|
-
}
|
|
1597
|
+
return {
|
|
1598
|
+
guid,
|
|
1599
|
+
cdate: this.entityCache[guid]['cdate'],
|
|
1600
|
+
mdate: this.entityCache[guid]['mdate'],
|
|
1601
|
+
tags: this.entityCache[guid]['tags'],
|
|
1602
|
+
data: this.entityCache[guid]['data'],
|
|
1603
|
+
sdata: this.entityCache[guid]['sdata'],
|
|
1604
|
+
};
|
|
1158
1605
|
}
|
|
1159
1606
|
return null;
|
|
1160
1607
|
}
|
|
@@ -1175,7 +1622,7 @@ export default abstract class NymphDriver {
|
|
|
1175
1622
|
mdate: number,
|
|
1176
1623
|
tags: string[],
|
|
1177
1624
|
data: EntityData,
|
|
1178
|
-
sdata: SerializedEntityData
|
|
1625
|
+
sdata: SerializedEntityData,
|
|
1179
1626
|
) {
|
|
1180
1627
|
if (guid == null) {
|
|
1181
1628
|
return;
|
|
@@ -1191,11 +1638,11 @@ export default abstract class NymphDriver {
|
|
|
1191
1638
|
}
|
|
1192
1639
|
// Cache the entity.
|
|
1193
1640
|
this.entityCache[guid] = {
|
|
1194
|
-
cdate
|
|
1195
|
-
mdate
|
|
1196
|
-
tags
|
|
1197
|
-
data
|
|
1198
|
-
sdata
|
|
1641
|
+
cdate,
|
|
1642
|
+
mdate,
|
|
1643
|
+
tags,
|
|
1644
|
+
data,
|
|
1645
|
+
sdata,
|
|
1199
1646
|
};
|
|
1200
1647
|
while (
|
|
1201
1648
|
this.nymph.config.cacheLimit &&
|
|
@@ -1204,7 +1651,7 @@ export default abstract class NymphDriver {
|
|
|
1204
1651
|
// Find which entity has been accessed the least.
|
|
1205
1652
|
const least =
|
|
1206
1653
|
Object.entries(this.entityCount)
|
|
1207
|
-
.sort(([
|
|
1654
|
+
.sort(([_guidA, countA], [_guidB, countB]) => countA - countB)
|
|
1208
1655
|
.pop()?.[0] ?? null;
|
|
1209
1656
|
if (least == null) {
|
|
1210
1657
|
// This should never happen.
|