@nymphjs/nymph 1.0.0-beta.6 → 1.0.0-beta.61
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 +277 -0
- package/README.md +13 -21
- package/dist/Entity.d.ts +269 -7
- package/dist/Entity.js +431 -89
- package/dist/Entity.js.map +1 -1
- package/dist/Entity.test.js +38 -16
- package/dist/Entity.test.js.map +1 -1
- package/dist/Entity.types.d.ts +225 -19
- package/dist/Nymph.d.ts +423 -11
- package/dist/Nymph.js +649 -156
- package/dist/Nymph.js.map +1 -1
- package/dist/Nymph.types.d.ts +31 -7
- package/dist/Nymph.types.js +2 -1
- package/dist/Nymph.types.js.map +1 -1
- package/dist/conf/d.d.ts +26 -0
- package/dist/conf/defaults.js +22 -0
- package/dist/conf/defaults.js.map +1 -1
- package/dist/driver/NymphDriver.d.ts +111 -36
- package/dist/driver/NymphDriver.js +169 -68
- package/dist/driver/NymphDriver.js.map +1 -1
- package/dist/driver/testArtifacts.d.ts +1 -1
- package/dist/driver/testArtifacts.js +285 -18
- package/dist/driver/testArtifacts.js.map +1 -1
- package/dist/errors/EntityInvalidDataError.d.ts +6 -0
- package/dist/errors/EntityInvalidDataError.js +6 -0
- package/dist/errors/EntityInvalidDataError.js.map +1 -1
- package/dist/errors/EntityUniqueConstraintError.d.ts +3 -0
- package/dist/errors/EntityUniqueConstraintError.js +11 -0
- package/dist/errors/EntityUniqueConstraintError.js.map +1 -0
- package/dist/errors/InvalidStateError.d.ts +3 -0
- package/dist/errors/InvalidStateError.js +11 -0
- package/dist/errors/InvalidStateError.js.map +1 -0
- package/dist/errors/QueryFailedError.js.map +1 -1
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/index.js.map +1 -1
- package/dist/testArtifacts.d.ts +17 -5
- package/dist/testArtifacts.js +23 -19
- package/dist/testArtifacts.js.map +1 -1
- package/dist/testMocks.d.ts +15 -10
- package/dist/testMocks.js +34 -11
- package/dist/testMocks.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +21 -5
- package/dist/utils.js.map +1 -1
- package/package.json +16 -14
- package/src/Entity.test.ts +30 -10
- package/src/Entity.ts +406 -135
- package/src/Entity.types.ts +32 -66
- package/src/Nymph.ts +626 -196
- package/src/Nymph.types.ts +97 -26
- package/src/conf/d.ts +8 -0
- package/src/conf/defaults.ts +24 -0
- package/src/driver/NymphDriver.ts +187 -243
- package/src/driver/testArtifacts.ts +327 -146
- package/src/errors/EntityUniqueConstraintError.ts +6 -0
- package/src/errors/InvalidStateError.ts +6 -0
- package/src/errors/index.ts +4 -0
- package/src/nymphJoiProps.ts +2 -2
- package/src/testArtifacts.ts +18 -19
- package/src/testMocks.ts +64 -32
- package/src/utils.ts +22 -8
- package/tsconfig.json +1 -1
- package/typedoc.json +4 -0
package/dist/Entity.js
CHANGED
|
@@ -3,28 +3,152 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const lodash_1 = require("lodash");
|
|
4
4
|
const errors_1 = require("./errors");
|
|
5
5
|
const utils_1 = require("./utils");
|
|
6
|
+
/**
|
|
7
|
+
* Database abstraction object.
|
|
8
|
+
*
|
|
9
|
+
* Provides a way to access, manipulate, and store data in Nymph.
|
|
10
|
+
*
|
|
11
|
+
* The GUID is not set until the entity is saved. GUIDs must be unique forever,
|
|
12
|
+
* even after deletion. It's the job of the Nymph DB driver to make sure no two
|
|
13
|
+
* entities ever have the same GUID. This is generally done by using a large
|
|
14
|
+
* randomly generated ID.
|
|
15
|
+
*
|
|
16
|
+
* Each entity class has an etype that determines which table(s) in the database
|
|
17
|
+
* it belongs to. If two entity classes have the same etype, their data will be
|
|
18
|
+
* stored in the same table(s). This isn't a good idea, however, because
|
|
19
|
+
* references to an entity store a class name, not an etype.
|
|
20
|
+
*
|
|
21
|
+
* Tags are used to classify entities. Where an etype is used to separate data
|
|
22
|
+
* by tables, tags can be used to separate entities within a table. You can
|
|
23
|
+
* define specific tags to be protected, meaning they cannot be added/removed
|
|
24
|
+
* from the client. It can be useful to allow user defined tags, such as for
|
|
25
|
+
* blog posts.
|
|
26
|
+
*
|
|
27
|
+
* Simply calling $delete() will not unset the entity. It will still take up
|
|
28
|
+
* memory. Likewise, simply calling unset will not delete the entity from the
|
|
29
|
+
* DB.
|
|
30
|
+
*
|
|
31
|
+
* Some notes about $equals() and $is(), the replacements for "==":
|
|
32
|
+
*
|
|
33
|
+
* The == operator will likely not give you the result you want, since two
|
|
34
|
+
* instances of the same entity will fail that check, even though they represent
|
|
35
|
+
* the same data in the database.
|
|
36
|
+
*
|
|
37
|
+
* $equals() performs a more strict comparison of the entity to another. Use
|
|
38
|
+
* $equals() instead of the == operator when you want to check both the entities
|
|
39
|
+
* they represent, and the data inside them. In order to return true for
|
|
40
|
+
* $equals(), the entity and object must meet the following criteria:
|
|
41
|
+
*
|
|
42
|
+
* - They must be entities.
|
|
43
|
+
* - They must have equal GUIDs, or both must have no GUID.
|
|
44
|
+
* - Their data and tags must be equal.
|
|
45
|
+
*
|
|
46
|
+
* $is() performs a less strict comparison of the entity to another. Use $is()
|
|
47
|
+
* instead of the == operator when the entity's data may have been changed, but
|
|
48
|
+
* you only care if they represent the same entity. In order to return true, the
|
|
49
|
+
* entity and object must meet the following criteria:
|
|
50
|
+
*
|
|
51
|
+
* - They must be entities.
|
|
52
|
+
* - They must have equal GUIDs, or both must have no GUID.
|
|
53
|
+
* - If they have no GUIDs, their data and tags must be equal.
|
|
54
|
+
*
|
|
55
|
+
* Some notes about saving entities in other entity's properties:
|
|
56
|
+
*
|
|
57
|
+
* Entities use references in the DB to store an entity in their properties. The
|
|
58
|
+
* reference is stored as an array with the values:
|
|
59
|
+
*
|
|
60
|
+
* - 0 => The string 'nymph_entity_reference'
|
|
61
|
+
* - 1 => The referenced entity's GUID.
|
|
62
|
+
* - 2 => The referenced entity's class name.
|
|
63
|
+
*
|
|
64
|
+
* Since the referenced entity's class name (meaning the `class` static
|
|
65
|
+
* property, not the name of the class itself) is stored in the reference on the
|
|
66
|
+
* parent entity, if you change the class name in an update, you need to
|
|
67
|
+
* reassign all referenced entities of that class and resave.
|
|
68
|
+
*
|
|
69
|
+
* When an entity is loaded, it does not request its referenced entities from
|
|
70
|
+
* Nymph. Instead, it creates instances without data called sleeping references.
|
|
71
|
+
* When you first access an entity's data, if it is a sleeping reference, it
|
|
72
|
+
* will fill its data from the DB. You can call clearCache() to turn all the
|
|
73
|
+
* entities back into sleeping references.
|
|
74
|
+
*/
|
|
6
75
|
class Entity {
|
|
7
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Initialize an entity.
|
|
78
|
+
*/
|
|
79
|
+
constructor(..._rest) {
|
|
8
80
|
this.guid = null;
|
|
9
81
|
this.cdate = null;
|
|
10
82
|
this.mdate = null;
|
|
11
83
|
this.tags = [];
|
|
84
|
+
/**
|
|
85
|
+
* Whether this instance is a sleeping reference.
|
|
86
|
+
*/
|
|
12
87
|
this.$isASleepingReference = false;
|
|
88
|
+
/**
|
|
89
|
+
* The reference to use to wake.
|
|
90
|
+
*/
|
|
13
91
|
this.$sleepingReference = null;
|
|
92
|
+
/**
|
|
93
|
+
* A promise that resolved when the entity's data is wake.
|
|
94
|
+
*/
|
|
95
|
+
this.$wakePromise = null;
|
|
96
|
+
/**
|
|
97
|
+
* Properties that will not be serialized into JSON with toJSON(). This
|
|
98
|
+
* can be considered a denylist, because these properties will not be set
|
|
99
|
+
* with incoming JSON.
|
|
100
|
+
*
|
|
101
|
+
* Clients CAN still determine what is in these properties, unless they are
|
|
102
|
+
* also listed in searchRestrictedData.
|
|
103
|
+
*/
|
|
14
104
|
this.$privateData = [];
|
|
105
|
+
/**
|
|
106
|
+
* Properties that can only be modified by server side code. They will still
|
|
107
|
+
* be visible on the frontend, unlike $privateData, but any changes to them
|
|
108
|
+
* that come from the frontend will be ignored.
|
|
109
|
+
*
|
|
110
|
+
* In addition to what's listed here, all of the access control properties
|
|
111
|
+
* will be included when Tilmeld is being used. These are:
|
|
112
|
+
*
|
|
113
|
+
* - acUser
|
|
114
|
+
* - acGroup
|
|
115
|
+
* - acOther
|
|
116
|
+
* - acRead
|
|
117
|
+
* - acWrite
|
|
118
|
+
* - acFull
|
|
119
|
+
* - user
|
|
120
|
+
* - group
|
|
121
|
+
*
|
|
122
|
+
* You should modify these through client enabled methods or the $save method
|
|
123
|
+
* instead, for safety.
|
|
124
|
+
*/
|
|
15
125
|
this.$protectedData = [];
|
|
126
|
+
/**
|
|
127
|
+
* Tags that can only be added/removed by server side code. They will still be
|
|
128
|
+
* visible on the frontend, but any changes to them that come from the
|
|
129
|
+
* frontend will be ignored.
|
|
130
|
+
*/
|
|
16
131
|
this.$protectedTags = [];
|
|
132
|
+
/**
|
|
133
|
+
* The names of methods allowed to be called by the frontend with serverCall.
|
|
134
|
+
*/
|
|
17
135
|
this.$clientEnabledMethods = [];
|
|
136
|
+
/**
|
|
137
|
+
* Whether to use "skipAc" when accessing entity references.
|
|
138
|
+
*/
|
|
18
139
|
this.$skipAc = false;
|
|
140
|
+
/**
|
|
141
|
+
* The AC properties' values when the entity was loaded.
|
|
142
|
+
*/
|
|
19
143
|
this.$originalAcValues = null;
|
|
20
144
|
this.$nymph = this.constructor.nymph;
|
|
21
145
|
this.$dataHandler = {
|
|
22
146
|
has: (data, name) => {
|
|
23
|
-
this.$
|
|
147
|
+
this.$check();
|
|
24
148
|
return name in data || name in this.$sdata;
|
|
25
149
|
},
|
|
26
150
|
get: (data, name) => {
|
|
27
|
-
this.$
|
|
151
|
+
this.$check();
|
|
28
152
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
29
153
|
data[name] = (0, utils_1.referencesToEntities)(JSON.parse(this.$sdata[name]), this.$nymph, this.$skipAc);
|
|
30
154
|
delete this.$sdata[name];
|
|
@@ -35,7 +159,7 @@ class Entity {
|
|
|
35
159
|
return undefined;
|
|
36
160
|
},
|
|
37
161
|
set: (data, name, value) => {
|
|
38
|
-
this.$
|
|
162
|
+
this.$check();
|
|
39
163
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
40
164
|
delete this.$sdata[name];
|
|
41
165
|
}
|
|
@@ -43,7 +167,7 @@ class Entity {
|
|
|
43
167
|
return true;
|
|
44
168
|
},
|
|
45
169
|
deleteProperty: (data, name) => {
|
|
46
|
-
this.$
|
|
170
|
+
this.$check();
|
|
47
171
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
48
172
|
return delete this.$sdata[name];
|
|
49
173
|
}
|
|
@@ -53,7 +177,7 @@ class Entity {
|
|
|
53
177
|
return true;
|
|
54
178
|
},
|
|
55
179
|
defineProperty: (data, name, descriptor) => {
|
|
56
|
-
this.$
|
|
180
|
+
this.$check();
|
|
57
181
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
58
182
|
delete this.$sdata[name];
|
|
59
183
|
}
|
|
@@ -61,7 +185,7 @@ class Entity {
|
|
|
61
185
|
return true;
|
|
62
186
|
},
|
|
63
187
|
getOwnPropertyDescriptor: (data, name) => {
|
|
64
|
-
this.$
|
|
188
|
+
this.$check();
|
|
65
189
|
if (this.$sdata.hasOwnProperty(name)) {
|
|
66
190
|
data[name] = (0, utils_1.referencesToEntities)(JSON.parse(this.$sdata[name]), this.$nymph, this.$skipAc);
|
|
67
191
|
delete this.$sdata[name];
|
|
@@ -69,23 +193,13 @@ class Entity {
|
|
|
69
193
|
return Object.getOwnPropertyDescriptor(data, name);
|
|
70
194
|
},
|
|
71
195
|
ownKeys: (data) => {
|
|
72
|
-
this.$
|
|
196
|
+
this.$check();
|
|
73
197
|
return Object.getOwnPropertyNames(data).concat(Object.getOwnPropertyNames(this.$sdata));
|
|
74
198
|
},
|
|
75
199
|
};
|
|
76
200
|
this.$dataStore = {};
|
|
77
201
|
this.$sdata = {};
|
|
78
202
|
this.$data = new Proxy(this.$dataStore, this.$dataHandler);
|
|
79
|
-
if (guid != null) {
|
|
80
|
-
const entity = this.$nymph.driver.getEntitySync({ class: this.constructor }, { type: '&', guid });
|
|
81
|
-
if (entity) {
|
|
82
|
-
this.guid = entity.guid;
|
|
83
|
-
this.tags = entity.tags;
|
|
84
|
-
this.cdate = entity.cdate;
|
|
85
|
-
this.mdate = entity.mdate;
|
|
86
|
-
this.$putData(entity.$getData(), entity.$getSData());
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
203
|
return new Proxy(this, {
|
|
90
204
|
has: (entity, name) => {
|
|
91
205
|
if (typeof name !== 'string' ||
|
|
@@ -93,7 +207,7 @@ class Entity {
|
|
|
93
207
|
name.substring(0, 1) === '$') {
|
|
94
208
|
return name in entity;
|
|
95
209
|
}
|
|
96
|
-
this.$
|
|
210
|
+
this.$check();
|
|
97
211
|
return name in entity.$data;
|
|
98
212
|
},
|
|
99
213
|
get: (entity, name) => {
|
|
@@ -101,11 +215,11 @@ class Entity {
|
|
|
101
215
|
name in entity ||
|
|
102
216
|
name.substring(0, 1) === '$') {
|
|
103
217
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
104
|
-
this.$
|
|
218
|
+
this.$check();
|
|
105
219
|
}
|
|
106
220
|
return entity[name];
|
|
107
221
|
}
|
|
108
|
-
this.$
|
|
222
|
+
this.$check();
|
|
109
223
|
if (name in entity.$data) {
|
|
110
224
|
return entity.$data[name];
|
|
111
225
|
}
|
|
@@ -116,12 +230,12 @@ class Entity {
|
|
|
116
230
|
name in entity ||
|
|
117
231
|
name.substring(0, 1) === '$') {
|
|
118
232
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
119
|
-
this.$
|
|
233
|
+
this.$check();
|
|
120
234
|
}
|
|
121
235
|
entity[name] = value;
|
|
122
236
|
}
|
|
123
237
|
else {
|
|
124
|
-
this.$
|
|
238
|
+
this.$check();
|
|
125
239
|
entity.$data[name] = value;
|
|
126
240
|
}
|
|
127
241
|
return true;
|
|
@@ -131,7 +245,7 @@ class Entity {
|
|
|
131
245
|
return delete entity[name];
|
|
132
246
|
}
|
|
133
247
|
else if (name in entity.$data) {
|
|
134
|
-
this.$
|
|
248
|
+
this.$check();
|
|
135
249
|
return delete entity.$data[name];
|
|
136
250
|
}
|
|
137
251
|
return true;
|
|
@@ -144,12 +258,12 @@ class Entity {
|
|
|
144
258
|
name in entity ||
|
|
145
259
|
name.substring(0, 1) === '$') {
|
|
146
260
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
147
|
-
this.$
|
|
261
|
+
this.$check();
|
|
148
262
|
}
|
|
149
263
|
Object.defineProperty(entity, name, descriptor);
|
|
150
264
|
}
|
|
151
265
|
else {
|
|
152
|
-
this.$
|
|
266
|
+
this.$check();
|
|
153
267
|
Object.defineProperty(entity.$data, name, descriptor);
|
|
154
268
|
}
|
|
155
269
|
return true;
|
|
@@ -159,21 +273,28 @@ class Entity {
|
|
|
159
273
|
name in entity ||
|
|
160
274
|
name.substring(0, 1) === '$') {
|
|
161
275
|
if (name === 'tags' || name === 'cdate' || name === 'mdate') {
|
|
162
|
-
this.$
|
|
276
|
+
this.$check();
|
|
163
277
|
}
|
|
164
278
|
return Object.getOwnPropertyDescriptor(entity, name);
|
|
165
279
|
}
|
|
166
280
|
else {
|
|
167
|
-
this.$
|
|
281
|
+
this.$check();
|
|
168
282
|
return Object.getOwnPropertyDescriptor(entity.$data, name);
|
|
169
283
|
}
|
|
170
284
|
},
|
|
171
285
|
ownKeys: (entity) => {
|
|
172
|
-
this.$
|
|
286
|
+
this.$check();
|
|
173
287
|
return Object.getOwnPropertyNames(entity).concat(Object.getOwnPropertyNames(entity.$data));
|
|
174
288
|
},
|
|
175
289
|
});
|
|
176
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Create or retrieve a new entity instance.
|
|
293
|
+
*
|
|
294
|
+
* Note that this will always return an entity, even if the GUID is not found.
|
|
295
|
+
*
|
|
296
|
+
* @param guid An optional GUID to retrieve.
|
|
297
|
+
*/
|
|
177
298
|
static async factory(guid) {
|
|
178
299
|
const entity = new this();
|
|
179
300
|
if (guid != null) {
|
|
@@ -186,14 +307,56 @@ class Entity {
|
|
|
186
307
|
}
|
|
187
308
|
return entity;
|
|
188
309
|
}
|
|
189
|
-
|
|
190
|
-
|
|
310
|
+
/**
|
|
311
|
+
* Create a new entity instance.
|
|
312
|
+
*/
|
|
313
|
+
static factorySync() {
|
|
314
|
+
return new this();
|
|
191
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Create a new sleeping reference instance.
|
|
318
|
+
*
|
|
319
|
+
* Sleeping references won't retrieve their data from the database until they
|
|
320
|
+
* are readied with `$wake()` or a parent's `$wakeAll()`.
|
|
321
|
+
*
|
|
322
|
+
* @param reference The Nymph Entity Reference to use to wake.
|
|
323
|
+
* @returns The new instance.
|
|
324
|
+
*/
|
|
192
325
|
static factoryReference(reference) {
|
|
193
326
|
const entity = new this();
|
|
194
327
|
entity.$referenceSleep(reference);
|
|
195
328
|
return entity;
|
|
196
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Get an array of strings that **must** be unique across the current etype.
|
|
332
|
+
*
|
|
333
|
+
* When you try to save another entity with any of the same unique strings,
|
|
334
|
+
* Nymph will throw an error.
|
|
335
|
+
*
|
|
336
|
+
* The default implementation of this static method instantiates the entity,
|
|
337
|
+
* assigns all of the given data, then calls `$getUniques` and returns its
|
|
338
|
+
* output. This can have a performance impact if a lot of extra processing
|
|
339
|
+
* happens during any of these steps. You can override this method to
|
|
340
|
+
* calculate the unique strings faster, but you must return the same strings
|
|
341
|
+
* that would be returned by `$getUniques`.
|
|
342
|
+
*
|
|
343
|
+
* @returns Resolves to an array of entity's unique constraint strings.
|
|
344
|
+
*/
|
|
345
|
+
static async getUniques({ guid, cdate, mdate, tags, data, sdata, }) {
|
|
346
|
+
const entity = new this();
|
|
347
|
+
if (guid != null) {
|
|
348
|
+
entity.guid = guid;
|
|
349
|
+
}
|
|
350
|
+
if (cdate != null) {
|
|
351
|
+
entity.cdate = cdate;
|
|
352
|
+
}
|
|
353
|
+
if (mdate != null) {
|
|
354
|
+
entity.mdate = mdate;
|
|
355
|
+
}
|
|
356
|
+
entity.tags = tags;
|
|
357
|
+
entity.$putData(data, sdata);
|
|
358
|
+
return await entity.$getUniques();
|
|
359
|
+
}
|
|
197
360
|
toJSON() {
|
|
198
361
|
if (this.$isASleepingReference) {
|
|
199
362
|
return this.$sleepingReference;
|
|
@@ -215,14 +378,14 @@ class Entity {
|
|
|
215
378
|
return obj;
|
|
216
379
|
}
|
|
217
380
|
$addTag(...tags) {
|
|
218
|
-
this.$
|
|
381
|
+
this.$check();
|
|
219
382
|
if (tags.length < 1) {
|
|
220
383
|
return;
|
|
221
384
|
}
|
|
222
385
|
this.tags = (0, utils_1.uniqueStrings)([...this.tags, ...tags]);
|
|
223
386
|
}
|
|
224
387
|
$arraySearch(array, strict = false) {
|
|
225
|
-
this.$
|
|
388
|
+
this.$check();
|
|
226
389
|
if (!Array.isArray(array)) {
|
|
227
390
|
return -1;
|
|
228
391
|
}
|
|
@@ -235,18 +398,18 @@ class Entity {
|
|
|
235
398
|
return -1;
|
|
236
399
|
}
|
|
237
400
|
$clearCache() {
|
|
238
|
-
this.$
|
|
401
|
+
this.$check();
|
|
239
402
|
this.$putData(this.$getData(), this.$getSData());
|
|
240
403
|
}
|
|
241
404
|
$getClientEnabledMethods() {
|
|
242
405
|
return this.$clientEnabledMethods;
|
|
243
406
|
}
|
|
244
407
|
async $delete() {
|
|
245
|
-
this.$
|
|
408
|
+
this.$check();
|
|
246
409
|
return await this.$nymph.deleteEntity(this);
|
|
247
410
|
}
|
|
248
411
|
$equals(object) {
|
|
249
|
-
this.$
|
|
412
|
+
this.$check();
|
|
250
413
|
if (!(object instanceof Entity)) {
|
|
251
414
|
return false;
|
|
252
415
|
}
|
|
@@ -255,22 +418,25 @@ class Entity {
|
|
|
255
418
|
return false;
|
|
256
419
|
}
|
|
257
420
|
}
|
|
258
|
-
if (object.constructor !== this.constructor) {
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
421
|
if (object.cdate !== this.cdate) {
|
|
262
422
|
return false;
|
|
263
423
|
}
|
|
264
424
|
if (object.mdate !== this.mdate) {
|
|
265
425
|
return false;
|
|
266
426
|
}
|
|
427
|
+
const obTags = [...object.tags].sort();
|
|
428
|
+
const myTags = [...this.tags].sort();
|
|
429
|
+
if (!(0, lodash_1.isEqual)(obTags, myTags)) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
267
432
|
const obData = object.$getData(true);
|
|
268
433
|
const myData = this.$getData(true);
|
|
269
434
|
return (0, lodash_1.isEqual)(obData, myData);
|
|
270
435
|
}
|
|
271
436
|
$getData(includeSData = false) {
|
|
272
|
-
this.$
|
|
437
|
+
this.$check();
|
|
273
438
|
if (includeSData) {
|
|
439
|
+
// Access all the serialized properties to initialize them.
|
|
274
440
|
for (const key in this.$sdata) {
|
|
275
441
|
const _unused = this[key];
|
|
276
442
|
}
|
|
@@ -278,24 +444,50 @@ class Entity {
|
|
|
278
444
|
return (0, utils_1.entitiesToReferences)({ ...this.$dataStore });
|
|
279
445
|
}
|
|
280
446
|
$getSData() {
|
|
281
|
-
this.$
|
|
447
|
+
this.$check();
|
|
282
448
|
return this.$sdata;
|
|
283
449
|
}
|
|
450
|
+
async $getUniques() {
|
|
451
|
+
return [];
|
|
452
|
+
}
|
|
284
453
|
$getOriginalAcValues() {
|
|
285
|
-
this.$
|
|
286
|
-
return
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
454
|
+
this.$check();
|
|
455
|
+
return this.$originalAcValues ?? this.$getCurrentAcValues();
|
|
456
|
+
}
|
|
457
|
+
$getCurrentAcValues() {
|
|
458
|
+
this.$check();
|
|
459
|
+
return {
|
|
460
|
+
user: this.$getAcUid(),
|
|
461
|
+
group: this.$getAcGid(),
|
|
462
|
+
acUser: this.$data.acUser ?? null,
|
|
463
|
+
acGroup: this.$data.acGroup ?? null,
|
|
464
|
+
acOther: this.$data.acOther ?? null,
|
|
465
|
+
acRead: this.$getAcReadIds(),
|
|
466
|
+
acWrite: this.$getAcWriteIds(),
|
|
467
|
+
acFull: this.$getAcFullIds(),
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
$getAcUid() {
|
|
471
|
+
return (this.$data.user?.guid ?? null);
|
|
472
|
+
}
|
|
473
|
+
$getAcGid() {
|
|
474
|
+
return (this.$data.group?.guid ?? null);
|
|
475
|
+
}
|
|
476
|
+
$getAcReadIds() {
|
|
477
|
+
return (this.$data.acRead?.map((entity) => entity.guid) ??
|
|
478
|
+
null);
|
|
479
|
+
}
|
|
480
|
+
$getAcWriteIds() {
|
|
481
|
+
return (this.$data.acWrite?.map((entity) => entity.guid) ??
|
|
482
|
+
null);
|
|
483
|
+
}
|
|
484
|
+
$getAcFullIds() {
|
|
485
|
+
return (this.$data.acFull?.map((entity) => entity.guid) ??
|
|
486
|
+
null);
|
|
296
487
|
}
|
|
297
488
|
$getValidatable() {
|
|
298
|
-
this.$
|
|
489
|
+
this.$check();
|
|
490
|
+
// Access all the serialized properties to initialize them.
|
|
299
491
|
for (const key in this.$sdata) {
|
|
300
492
|
const _unused = this[key];
|
|
301
493
|
}
|
|
@@ -308,11 +500,11 @@ class Entity {
|
|
|
308
500
|
};
|
|
309
501
|
}
|
|
310
502
|
$getTags() {
|
|
311
|
-
this.$
|
|
503
|
+
this.$check();
|
|
312
504
|
return this.tags;
|
|
313
505
|
}
|
|
314
506
|
$hasTag(...tags) {
|
|
315
|
-
this.$
|
|
507
|
+
this.$check();
|
|
316
508
|
if (!tags.length) {
|
|
317
509
|
return false;
|
|
318
510
|
}
|
|
@@ -333,27 +525,34 @@ class Entity {
|
|
|
333
525
|
if (this.guid || object.guid) {
|
|
334
526
|
return this.guid === object.guid;
|
|
335
527
|
}
|
|
336
|
-
this.$
|
|
528
|
+
this.$check();
|
|
337
529
|
if (typeof object.$getData !== 'function') {
|
|
338
530
|
return false;
|
|
339
531
|
}
|
|
340
532
|
else {
|
|
533
|
+
const obTags = [...object.tags].sort();
|
|
534
|
+
const myTags = [...this.tags].sort();
|
|
535
|
+
if (!(0, lodash_1.isEqual)(obTags, myTags)) {
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
341
538
|
const obData = object.$getData(true);
|
|
342
539
|
const myData = this.$getData(true);
|
|
343
540
|
return (0, lodash_1.isEqual)(obData, myData);
|
|
344
541
|
}
|
|
345
542
|
}
|
|
346
543
|
$jsonAcceptData(input, allowConflict = false) {
|
|
347
|
-
this.$
|
|
544
|
+
this.$check();
|
|
348
545
|
if (this.guid != input.guid) {
|
|
349
546
|
throw new errors_1.EntityConflictError('Tried to accept JSON input for the wrong entity.');
|
|
350
547
|
}
|
|
548
|
+
// Accept the modified date.
|
|
351
549
|
const mdate = input.mdate ?? 0;
|
|
352
550
|
const thismdate = this.mdate ?? 0;
|
|
353
551
|
if (mdate < thismdate && !allowConflict) {
|
|
354
552
|
throw new errors_1.EntityConflictError('This entity is newer than JSON input.');
|
|
355
553
|
}
|
|
356
554
|
this.mdate = input.mdate;
|
|
555
|
+
// Accept the tags.
|
|
357
556
|
const currentTags = this.$getTags();
|
|
358
557
|
const protectedTags = (0, lodash_1.intersection)(this.$protectedTags, currentTags);
|
|
359
558
|
let tags = (0, lodash_1.difference)(input.tags, this.$protectedTags);
|
|
@@ -362,6 +561,7 @@ class Entity {
|
|
|
362
561
|
}
|
|
363
562
|
this.$removeTag(...currentTags);
|
|
364
563
|
this.$addTag(...protectedTags, ...tags);
|
|
564
|
+
// Accept the data.
|
|
365
565
|
const data = input.data;
|
|
366
566
|
const privateData = {};
|
|
367
567
|
for (const name of this.$privateData) {
|
|
@@ -374,10 +574,21 @@ class Entity {
|
|
|
374
574
|
}
|
|
375
575
|
const protectedData = {};
|
|
376
576
|
const protectedProps = [...this.$protectedData];
|
|
377
|
-
if (this.$nymph.tilmeld
|
|
378
|
-
|
|
379
|
-
protectedProps.push('
|
|
380
|
-
protectedProps.push('
|
|
577
|
+
if (this.$nymph.tilmeld) {
|
|
578
|
+
protectedProps.push('acUser');
|
|
579
|
+
protectedProps.push('acGroup');
|
|
580
|
+
protectedProps.push('acOther');
|
|
581
|
+
protectedProps.push('acRead');
|
|
582
|
+
protectedProps.push('acWrite');
|
|
583
|
+
protectedProps.push('acFull');
|
|
584
|
+
if ((this.constructor.class !== 'User' &&
|
|
585
|
+
this.constructor.class !== 'Group') ||
|
|
586
|
+
!this.$nymph.tilmeld.currentUser ||
|
|
587
|
+
(!this.$nymph.tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
|
|
588
|
+
!this.$nymph.tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
589
|
+
protectedProps.push('user');
|
|
590
|
+
protectedProps.push('group');
|
|
591
|
+
}
|
|
381
592
|
}
|
|
382
593
|
for (const name of protectedProps) {
|
|
383
594
|
if (name in this.$data) {
|
|
@@ -407,20 +618,38 @@ class Entity {
|
|
|
407
618
|
});
|
|
408
619
|
}
|
|
409
620
|
$jsonAcceptPatch(patch, allowConflict = false) {
|
|
410
|
-
this.$
|
|
621
|
+
this.$check();
|
|
411
622
|
if (this.guid != patch.guid) {
|
|
412
623
|
throw new errors_1.EntityConflictError('Tried to accept JSON patch for the wrong entity.');
|
|
413
624
|
}
|
|
625
|
+
// Accept the modified date.
|
|
414
626
|
const mdate = patch.mdate ?? 0;
|
|
415
627
|
const thismdate = this.mdate ?? 0;
|
|
416
628
|
if (mdate < thismdate && !allowConflict) {
|
|
417
629
|
throw new errors_1.EntityConflictError('This entity is newer than JSON patch.');
|
|
418
630
|
}
|
|
419
631
|
this.mdate = patch.mdate;
|
|
632
|
+
const protectedProps = [...this.$protectedData];
|
|
633
|
+
if (this.$nymph.tilmeld) {
|
|
634
|
+
protectedProps.push('acUser');
|
|
635
|
+
protectedProps.push('acGroup');
|
|
636
|
+
protectedProps.push('acOther');
|
|
637
|
+
protectedProps.push('acRead');
|
|
638
|
+
protectedProps.push('acWrite');
|
|
639
|
+
protectedProps.push('acFull');
|
|
640
|
+
if ((this.constructor.class !== 'User' &&
|
|
641
|
+
this.constructor.class !== 'Group') ||
|
|
642
|
+
!this.$nymph.tilmeld.currentUser ||
|
|
643
|
+
(!this.$nymph.tilmeld.currentUser.abilities?.includes('tilmeld/admin') &&
|
|
644
|
+
!this.$nymph.tilmeld.currentUser.abilities?.includes('system/admin'))) {
|
|
645
|
+
protectedProps.push('user');
|
|
646
|
+
protectedProps.push('group');
|
|
647
|
+
}
|
|
648
|
+
}
|
|
420
649
|
for (const name in patch.set) {
|
|
421
650
|
if ((this.$allowlistData != null &&
|
|
422
651
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
423
|
-
|
|
652
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
424
653
|
this.$privateData.indexOf(name) !== -1) {
|
|
425
654
|
continue;
|
|
426
655
|
}
|
|
@@ -429,7 +658,7 @@ class Entity {
|
|
|
429
658
|
for (const name of patch.unset) {
|
|
430
659
|
if ((this.$allowlistData != null &&
|
|
431
660
|
this.$allowlistData.indexOf(name) === -1) ||
|
|
432
|
-
|
|
661
|
+
protectedProps.indexOf(name) !== -1 ||
|
|
433
662
|
this.$privateData.indexOf(name) !== -1) {
|
|
434
663
|
continue;
|
|
435
664
|
}
|
|
@@ -453,7 +682,7 @@ class Entity {
|
|
|
453
682
|
}
|
|
454
683
|
}
|
|
455
684
|
$putData(data, sdata) {
|
|
456
|
-
this.$
|
|
685
|
+
this.$check();
|
|
457
686
|
const mySdata = sdata ?? this.$getSData();
|
|
458
687
|
for (const name in data) {
|
|
459
688
|
delete mySdata[name];
|
|
@@ -465,10 +694,15 @@ class Entity {
|
|
|
465
694
|
this.$dataStore[name] = (0, utils_1.referencesToEntities)(data[name], this.$nymph, this.$skipAc);
|
|
466
695
|
}
|
|
467
696
|
this.$sdata = mySdata;
|
|
697
|
+
// Set original AC values if not set..
|
|
468
698
|
if (this.$originalAcValues == null) {
|
|
469
|
-
this.$originalAcValues = this.$
|
|
699
|
+
this.$originalAcValues = this.$getCurrentAcValues();
|
|
470
700
|
}
|
|
471
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* Set up a sleeping reference.
|
|
704
|
+
* @param array $reference The reference to use to wake.
|
|
705
|
+
*/
|
|
472
706
|
$referenceSleep(reference) {
|
|
473
707
|
if (reference.length !== 3 ||
|
|
474
708
|
reference[0] !== 'nymph_entity_reference' ||
|
|
@@ -486,34 +720,108 @@ class Entity {
|
|
|
486
720
|
this.guid = reference[1];
|
|
487
721
|
this.$sleepingReference = reference;
|
|
488
722
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
723
|
+
/**
|
|
724
|
+
* Check if this is a sleeping reference and throw an error if so.
|
|
725
|
+
*/
|
|
726
|
+
$check() {
|
|
727
|
+
if (this.$isASleepingReference || this.$sleepingReference != null) {
|
|
728
|
+
throw new errors_1.EntityIsSleepingReferenceError('This entity is in a sleeping reference state. You must use .$wake() to wake it.');
|
|
492
729
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Check if this is a sleeping reference.
|
|
733
|
+
*/
|
|
734
|
+
$asleep() {
|
|
735
|
+
return this.$isASleepingReference || this.$sleepingReference != null;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Wake from a sleeping reference.
|
|
739
|
+
*/
|
|
740
|
+
$wake() {
|
|
741
|
+
if (!this.$isASleepingReference || this.$sleepingReference == null) {
|
|
742
|
+
this.$wakePromise = null;
|
|
743
|
+
return Promise.resolve(this);
|
|
744
|
+
}
|
|
745
|
+
if (this.$sleepingReference?.[1] == null) {
|
|
746
|
+
throw new errors_1.InvalidStateError('Tried to wake a sleeping reference with no GUID.');
|
|
747
|
+
}
|
|
748
|
+
if (!this.$wakePromise) {
|
|
749
|
+
const EntityClass = this.$nymph.getEntityClass(this.$sleepingReference[2]);
|
|
750
|
+
this.$wakePromise = this.$nymph
|
|
751
|
+
.getEntity({
|
|
752
|
+
class: EntityClass,
|
|
753
|
+
skipAc: this.$skipAc,
|
|
754
|
+
}, { type: '&', guid: this.$sleepingReference[1] })
|
|
755
|
+
.then((entity) => {
|
|
756
|
+
if (entity == null || entity.guid == null) {
|
|
757
|
+
return Promise.reject(new errors_1.InvalidStateError('The sleeping reference could not be retrieved.'));
|
|
758
|
+
}
|
|
759
|
+
this.$isASleepingReference = false;
|
|
760
|
+
this.$sleepingReference = null;
|
|
761
|
+
this.guid = entity.guid;
|
|
762
|
+
this.tags = entity.tags;
|
|
763
|
+
this.cdate = entity.cdate;
|
|
764
|
+
this.mdate = entity.mdate;
|
|
765
|
+
this.$putData(entity.$getData(), entity.$getSData());
|
|
766
|
+
return this;
|
|
767
|
+
})
|
|
768
|
+
.finally(() => {
|
|
769
|
+
this.$wakePromise = null;
|
|
770
|
+
});
|
|
500
771
|
}
|
|
501
|
-
this.$
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
772
|
+
return this.$wakePromise;
|
|
773
|
+
}
|
|
774
|
+
$wakeAll(level) {
|
|
775
|
+
return new Promise((resolve, reject) => {
|
|
776
|
+
// Run this once this entity is wake.
|
|
777
|
+
const wakeProps = () => {
|
|
778
|
+
let newLevel;
|
|
779
|
+
// If level is undefined, keep going forever, otherwise, stop once we've
|
|
780
|
+
// gone deep enough.
|
|
781
|
+
if (level !== undefined) {
|
|
782
|
+
newLevel = level - 1;
|
|
783
|
+
}
|
|
784
|
+
if (newLevel !== undefined && newLevel < 0) {
|
|
785
|
+
resolve(this);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const promises = [];
|
|
789
|
+
// Go through data looking for entities to wake.
|
|
790
|
+
for (let [key, value] of Object.entries(this.$data)) {
|
|
791
|
+
if (value instanceof Entity && value.$isASleepingReference) {
|
|
792
|
+
promises.push(value.$wakeAll(newLevel));
|
|
793
|
+
}
|
|
794
|
+
else if (Array.isArray(value)) {
|
|
795
|
+
for (let i = 0; i < value.length; i++) {
|
|
796
|
+
if (value[i] instanceof Entity &&
|
|
797
|
+
value[i].$isASleepingReference) {
|
|
798
|
+
promises.push(value[i].$wakeAll(newLevel));
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
if (promises.length) {
|
|
804
|
+
Promise.all(promises).then(() => resolve(this), (errObj) => reject(errObj));
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
resolve(this);
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
if (this.$isASleepingReference) {
|
|
811
|
+
this.$wake().then(wakeProps, (errObj) => reject(errObj));
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
wakeProps();
|
|
815
|
+
}
|
|
816
|
+
});
|
|
509
817
|
}
|
|
510
818
|
async $refresh() {
|
|
511
|
-
this.$
|
|
819
|
+
await this.$wake();
|
|
512
820
|
if (this.guid == null) {
|
|
513
821
|
return false;
|
|
514
822
|
}
|
|
515
823
|
const refresh = await this.$nymph.getEntity({
|
|
516
|
-
class: this.constructor,
|
|
824
|
+
class: this.$nymph.getEntityClass(this.constructor),
|
|
517
825
|
skipCache: true,
|
|
518
826
|
skipAc: this.$skipAc,
|
|
519
827
|
}, { type: '&', guid: this.guid });
|
|
@@ -527,11 +835,11 @@ class Entity {
|
|
|
527
835
|
return true;
|
|
528
836
|
}
|
|
529
837
|
$removeTag(...tags) {
|
|
530
|
-
this.$
|
|
838
|
+
this.$check();
|
|
531
839
|
this.tags = (0, lodash_1.difference)(this.tags, tags);
|
|
532
840
|
}
|
|
533
841
|
async $save() {
|
|
534
|
-
this.$
|
|
842
|
+
await this.$wake();
|
|
535
843
|
return await this.$nymph.saveEntity(this);
|
|
536
844
|
}
|
|
537
845
|
$toReference() {
|
|
@@ -551,10 +859,44 @@ class Entity {
|
|
|
551
859
|
this.$skipAc = !!skipAc;
|
|
552
860
|
}
|
|
553
861
|
}
|
|
554
|
-
|
|
862
|
+
/**
|
|
863
|
+
* The instance of Nymph to use for queries.
|
|
864
|
+
*/
|
|
555
865
|
Entity.nymph = {};
|
|
866
|
+
/**
|
|
867
|
+
* A unique name for this type of entity used to separate its data from other
|
|
868
|
+
* types of entities in the database.
|
|
869
|
+
*/
|
|
556
870
|
Entity.ETYPE = 'entity';
|
|
871
|
+
/**
|
|
872
|
+
* The lookup name for this entity.
|
|
873
|
+
*
|
|
874
|
+
* This is used for reference arrays (and sleeping references) and client
|
|
875
|
+
* requests.
|
|
876
|
+
*/
|
|
557
877
|
Entity.class = 'Entity';
|
|
878
|
+
/**
|
|
879
|
+
* Whether this entity should publish changes to PubSub servers.
|
|
880
|
+
*/
|
|
881
|
+
Entity.pubSubEnabled = true;
|
|
882
|
+
/**
|
|
883
|
+
* Whether this entity should be accessible on the frontend through the REST
|
|
884
|
+
* server.
|
|
885
|
+
*
|
|
886
|
+
* If this is false, any request from the client that attempts to use this
|
|
887
|
+
* entity will fail.
|
|
888
|
+
*/
|
|
889
|
+
Entity.restEnabled = true;
|
|
890
|
+
/**
|
|
891
|
+
* Properties that will not be searchable from the frontend. If the frontend
|
|
892
|
+
* includes any of these properties in any of their clauses, they will be
|
|
893
|
+
* filtered out before the search is executed.
|
|
894
|
+
*/
|
|
558
895
|
Entity.searchRestrictedData = [];
|
|
896
|
+
/**
|
|
897
|
+
* The names of static methods allowed to be called by the frontend with
|
|
898
|
+
* serverCallStatic.
|
|
899
|
+
*/
|
|
559
900
|
Entity.clientEnabledStaticMethods = [];
|
|
901
|
+
exports.default = Entity;
|
|
560
902
|
//# sourceMappingURL=Entity.js.map
|