@karmaniverous/entity-manager 4.2.1 → 4.3.0-1
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/README.md
CHANGED
|
@@ -364,15 +364,16 @@ Query an entity across shards.
|
|
|
364
364
|
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
365
365
|
**Returns**: <code>Promise.<ShardedQueryResult></code> - Sharded query result.
|
|
366
366
|
|
|
367
|
-
| Param | Type |
|
|
368
|
-
| --- | --- | --- |
|
|
369
|
-
| options | <code>object</code> |
|
|
370
|
-
| options.entityToken | <code>string</code> |
|
|
371
|
-
| [options.keyToken] | <code>string</code> |
|
|
372
|
-
| [options.item] | <code>object</code> |
|
|
373
|
-
| options.shardQuery | <code>ShardQueryFunction</code> |
|
|
374
|
-
| [options.limit] | <code>number</code> |
|
|
375
|
-
| [options.pageKeys] | <code>object</code> |
|
|
367
|
+
| Param | Type | Description |
|
|
368
|
+
| --- | --- | --- |
|
|
369
|
+
| options | <code>object</code> | Query options. |
|
|
370
|
+
| options.entityToken | <code>string</code> | Entity token. |
|
|
371
|
+
| [options.keyToken] | <code>string</code> | Key token. |
|
|
372
|
+
| [options.item] | <code>object</code> | Entity item sufficiently populated to generate property keyToken. |
|
|
373
|
+
| options.shardQuery | <code>ShardQueryFunction</code> | Sharded query function. |
|
|
374
|
+
| [options.limit] | <code>number</code> | Request limit. |
|
|
375
|
+
| [options.pageKeys] | <code>object</code> | Map of shard page keys. |
|
|
376
|
+
| [options.pages] | <code>number</code> \| <code>'all'</code> | Max number of pages to query. |
|
|
376
377
|
|
|
377
378
|
<a name="module_entity-manager.EntityManager+rehydrateIndex"></a>
|
|
378
379
|
|
|
@@ -9,6 +9,8 @@ var _map2 = _interopRequireDefault(require("lodash/map"));
|
|
|
9
9
|
var _size2 = _interopRequireDefault(require("lodash/size"));
|
|
10
10
|
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
|
|
11
11
|
var _fromPairs2 = _interopRequireDefault(require("lodash/fromPairs"));
|
|
12
|
+
var _parseInt2 = _interopRequireDefault(require("lodash/parseInt"));
|
|
13
|
+
var _isString2 = _interopRequireDefault(require("lodash/isString"));
|
|
12
14
|
var _isPlainObject2 = _interopRequireDefault(require("lodash/isPlainObject"));
|
|
13
15
|
var _isInteger2 = _interopRequireDefault(require("lodash/isInteger"));
|
|
14
16
|
var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined"));
|
|
@@ -25,20 +27,18 @@ var _cloneDeep2 = _interopRequireDefault(require("lodash/cloneDeep"));
|
|
|
25
27
|
var _inspectParametersDeclaration = require("inspect-parameters-declaration");
|
|
26
28
|
var _PrivateEntityManager = require("./PrivateEntityManager.js");
|
|
27
29
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
|
|
33
|
-
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
|
|
34
|
-
function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
|
|
35
|
-
var _entityManager = /*#__PURE__*/new WeakMap();
|
|
30
|
+
/**
|
|
31
|
+
* @module entity-manager
|
|
32
|
+
*/
|
|
33
|
+
|
|
36
34
|
/**
|
|
37
35
|
* Manage DynamoDb entities.
|
|
38
36
|
*
|
|
39
37
|
* @class
|
|
40
38
|
*/
|
|
41
39
|
class EntityManager {
|
|
40
|
+
#entityManager;
|
|
41
|
+
|
|
42
42
|
/**
|
|
43
43
|
* Create an EntityManager instance.
|
|
44
44
|
*
|
|
@@ -54,14 +54,10 @@ class EntityManager {
|
|
|
54
54
|
config,
|
|
55
55
|
logger
|
|
56
56
|
} = _ref;
|
|
57
|
-
|
|
58
|
-
writable: true,
|
|
59
|
-
value: void 0
|
|
60
|
-
});
|
|
61
|
-
_classPrivateFieldSet(this, _entityManager, new _PrivateEntityManager.PrivateEntityManager({
|
|
57
|
+
this.#entityManager = new _PrivateEntityManager.PrivateEntityManager({
|
|
62
58
|
config,
|
|
63
59
|
logger
|
|
64
|
-
})
|
|
60
|
+
});
|
|
65
61
|
}
|
|
66
62
|
|
|
67
63
|
/**
|
|
@@ -69,17 +65,17 @@ class EntityManager {
|
|
|
69
65
|
*
|
|
70
66
|
* @param {string} entityToken - Entity token.
|
|
71
67
|
* @param {object} item - Entity item.
|
|
72
|
-
* @param {boolean} [overwrite
|
|
68
|
+
* @param {boolean} [overwrite] - Overwrite existing properties.
|
|
73
69
|
* @returns {object} Decorated entity item.
|
|
74
70
|
* @throws {Error} If entityToken is invalid.
|
|
75
71
|
* @throws {Error} If item is invalid.
|
|
76
72
|
*/
|
|
77
73
|
addKeys(entityToken, item) {
|
|
78
74
|
let overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
79
|
-
|
|
75
|
+
this.#entityManager.logger.debug(`adding sharded index keys to ${entityToken}${overwrite ? ' with overwrite' : ''}...`, item);
|
|
80
76
|
|
|
81
77
|
// Validate item.
|
|
82
|
-
|
|
78
|
+
this.#entityManager.validateItem(item);
|
|
83
79
|
|
|
84
80
|
// Clone item.
|
|
85
81
|
const newItem = (0, _cloneDeep2.default)(item);
|
|
@@ -87,10 +83,10 @@ class EntityManager {
|
|
|
87
83
|
// Get entity config.
|
|
88
84
|
const {
|
|
89
85
|
shardKeyToken
|
|
90
|
-
} =
|
|
86
|
+
} = this.#entityManager;
|
|
91
87
|
const {
|
|
92
88
|
keys
|
|
93
|
-
} =
|
|
89
|
+
} = this.#entityManager.getEntityConfig(entityToken);
|
|
94
90
|
|
|
95
91
|
// Add shardKey.
|
|
96
92
|
if (overwrite || (0, _isNil2.default)(newItem[shardKeyToken])) {
|
|
@@ -104,7 +100,7 @@ class EntityManager {
|
|
|
104
100
|
} = _ref2;
|
|
105
101
|
if (overwrite || (0, _isNil2.default)(newItem[key])) newItem[key] = encode(newItem);
|
|
106
102
|
});
|
|
107
|
-
|
|
103
|
+
this.#entityManager.logger.debug('done', newItem);
|
|
108
104
|
|
|
109
105
|
// Remove undefined shardKey.
|
|
110
106
|
if ((0, _isNil2.default)(newItem[shardKeyToken])) delete newItem[shardKeyToken];
|
|
@@ -121,10 +117,10 @@ class EntityManager {
|
|
|
121
117
|
calcShardKey(entityToken, item) {
|
|
122
118
|
const {
|
|
123
119
|
sharding
|
|
124
|
-
} =
|
|
120
|
+
} = this.#entityManager.getEntityConfig(entityToken);
|
|
125
121
|
const entityKey = sharding.entityKey(item);
|
|
126
122
|
const timestamp = sharding.timestamp(item);
|
|
127
|
-
return
|
|
123
|
+
return this.#entityManager.getShardKey(entityToken, entityKey, timestamp);
|
|
128
124
|
}
|
|
129
125
|
|
|
130
126
|
/**
|
|
@@ -142,8 +138,8 @@ class EntityManager {
|
|
|
142
138
|
dehydrateIndex(entityToken, indexToken, index) {
|
|
143
139
|
let delimiter = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '~';
|
|
144
140
|
// Validate item.
|
|
145
|
-
|
|
146
|
-
const indexComponents = (0, _isArray2.default)(indexToken) ? indexToken :
|
|
141
|
+
this.#entityManager.validateItem(index);
|
|
142
|
+
const indexComponents = (0, _isArray2.default)(indexToken) ? indexToken : this.#entityManager.getIndexComponents(entityToken, indexToken);
|
|
147
143
|
const indexProperties = indexComponents.reduce((properties, component) => {
|
|
148
144
|
const {
|
|
149
145
|
decode
|
|
@@ -176,7 +172,7 @@ class EntityManager {
|
|
|
176
172
|
getKey(entityToken, keyToken) {
|
|
177
173
|
const {
|
|
178
174
|
keys
|
|
179
|
-
} =
|
|
175
|
+
} = this.#entityManager.getEntityConfig(entityToken);
|
|
180
176
|
if (!(0, _has2.default)(keys, keyToken)) throw new Error(`Key '${keyToken}' does not exist for entity '${entityToken}'.`);
|
|
181
177
|
return keys[keyToken];
|
|
182
178
|
}
|
|
@@ -196,13 +192,13 @@ class EntityManager {
|
|
|
196
192
|
*/
|
|
197
193
|
getKeySpace(entityToken, keyToken, item) {
|
|
198
194
|
let timestamp = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Date.now();
|
|
199
|
-
|
|
200
|
-
const shardKeySpace =
|
|
195
|
+
this.#entityManager.logger.debug(`getting shard key space for ${entityToken} on key '${keyToken}' at timestamp ${timestamp}...`, item);
|
|
196
|
+
const shardKeySpace = this.#entityManager.getShardKeySpace(entityToken, timestamp);
|
|
201
197
|
const result = (0, _sortedUniq2.default)(shardKeySpace.map(shardKey => this.getKey(entityToken, keyToken).encode({
|
|
202
198
|
...item,
|
|
203
|
-
[
|
|
199
|
+
[this.#entityManager.shardKeyToken]: shardKey
|
|
204
200
|
})));
|
|
205
|
-
|
|
201
|
+
this.#entityManager.logger.debug('done', result);
|
|
206
202
|
return result;
|
|
207
203
|
}
|
|
208
204
|
|
|
@@ -233,11 +229,12 @@ class EntityManager {
|
|
|
233
229
|
*
|
|
234
230
|
* @param {object} options - Query options.
|
|
235
231
|
* @param {string} options.entityToken - Entity token.
|
|
236
|
-
* @param {string} [options.keyToken
|
|
237
|
-
* @param {object} [options.item
|
|
232
|
+
* @param {string} [options.keyToken] - Key token.
|
|
233
|
+
* @param {object} [options.item] - Entity item sufficiently populated to generate property keyToken.
|
|
238
234
|
* @param {ShardQueryFunction} options.shardQuery - Sharded query function.
|
|
239
235
|
* @param {number} [options.limit] - Request limit.
|
|
240
236
|
* @param {object} [options.pageKeys] - Map of shard page keys.
|
|
237
|
+
* @param {number|'all'} [options.pages] - Max number of pages to query.
|
|
241
238
|
* @returns {Promise<ShardedQueryResult>} Sharded query result.
|
|
242
239
|
*/
|
|
243
240
|
async query() {
|
|
@@ -247,14 +244,19 @@ class EntityManager {
|
|
|
247
244
|
item = {},
|
|
248
245
|
shardQuery,
|
|
249
246
|
limit,
|
|
250
|
-
pageKeys
|
|
247
|
+
pageKeys,
|
|
248
|
+
pages = 1
|
|
251
249
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
252
250
|
// Validate params.
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
this.#entityManager.validateKeyToken(entityToken, keyToken);
|
|
252
|
+
this.#entityManager.validateItem(item);
|
|
255
253
|
if (!(0, _isFunction2.default)(shardQuery)) throw new Error('shardQuery must be a function');
|
|
256
|
-
if (!(0, _isUndefined2.default)(limit) && !(0, _isInteger2.default)(limit)) throw new Error('limit must be
|
|
254
|
+
if (!(0, _isUndefined2.default)(limit) && !((0, _isInteger2.default)(limit) && limit >= 1)) throw new Error('limit must be a positive integer');
|
|
257
255
|
if (!(0, _isUndefined2.default)(pageKeys) && !(0, _isPlainObject2.default)(pageKeys)) throw new Error('pageKeys must be an object');
|
|
256
|
+
if (!(0, _isUndefined2.default)(pages) && !/^(?:\d+|all)$/i.test(pages.toString())) throw new Error("pages must be a positive integer or 'all'");
|
|
257
|
+
|
|
258
|
+
// Parse pages.
|
|
259
|
+
if ((0, _isString2.default)(pages)) pages = pages.toLowerCase() === 'all' ? Infinity : (0, _parseInt2.default)(pages);
|
|
258
260
|
|
|
259
261
|
// Generate default pageKeys if not provided
|
|
260
262
|
pageKeys ??= (0, _fromPairs2.default)(this.getKeySpace(entityToken, keyToken, item).map(shardedKey => [shardedKey, undefined]));
|
|
@@ -268,44 +270,57 @@ class EntityManager {
|
|
|
268
270
|
// Calculate shard limit.
|
|
269
271
|
const shardLimit = (0, _isUndefined2.default)(limit) ? undefined : Math.ceil(limit / (0, _size2.default)(pageKeys));
|
|
270
272
|
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
count,
|
|
275
|
-
items,
|
|
276
|
-
pageKey
|
|
277
|
-
} = _ref6;
|
|
278
|
-
return resolve({
|
|
279
|
-
count,
|
|
280
|
-
items,
|
|
281
|
-
pageKey,
|
|
282
|
-
shardedKey
|
|
283
|
-
});
|
|
284
|
-
}))));
|
|
285
|
-
|
|
286
|
-
// Reduce shardQueryResults into a single result.
|
|
287
|
-
const result = shardQueryResults.reduce((shardedQueryResult, _ref7) => {
|
|
288
|
-
let {
|
|
289
|
-
count,
|
|
290
|
-
items,
|
|
291
|
-
pageKey,
|
|
292
|
-
shardedKey
|
|
293
|
-
} = _ref7;
|
|
294
|
-
return {
|
|
295
|
-
count: shardedQueryResult.count + count,
|
|
296
|
-
items: [...shardedQueryResult.items, ...items],
|
|
297
|
-
pageKeys: {
|
|
298
|
-
...shardedQueryResult.pageKeys,
|
|
299
|
-
...((0, _isUndefined2.default)(pageKey) ? {} : {
|
|
300
|
-
[shardedKey]: pageKey
|
|
301
|
-
})
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
}, {
|
|
273
|
+
// Iterate search over pages.
|
|
274
|
+
let page = 0;
|
|
275
|
+
const result = {
|
|
305
276
|
count: 0,
|
|
306
277
|
items: [],
|
|
307
|
-
pageKeys
|
|
308
|
-
}
|
|
278
|
+
pageKeys
|
|
279
|
+
};
|
|
280
|
+
do {
|
|
281
|
+
// Query every shard in pageKeys.
|
|
282
|
+
const shardQueryResults = await Promise.all((0, _map2.default)(result.pageKeys, (pageKey, shardedKey) => new Promise(resolve => shardQuery(shardedKey, pageKey, shardLimit).then(_ref6 => {
|
|
283
|
+
let {
|
|
284
|
+
count,
|
|
285
|
+
items,
|
|
286
|
+
pageKey
|
|
287
|
+
} = _ref6;
|
|
288
|
+
return resolve({
|
|
289
|
+
count,
|
|
290
|
+
items,
|
|
291
|
+
pageKey,
|
|
292
|
+
shardedKey
|
|
293
|
+
});
|
|
294
|
+
}))));
|
|
295
|
+
|
|
296
|
+
// Reduce shardQueryResults into a single result.
|
|
297
|
+
const pageResult = shardQueryResults.reduce((shardedQueryResult, _ref7) => {
|
|
298
|
+
let {
|
|
299
|
+
count,
|
|
300
|
+
items,
|
|
301
|
+
pageKey,
|
|
302
|
+
shardedKey
|
|
303
|
+
} = _ref7;
|
|
304
|
+
return {
|
|
305
|
+
count: shardedQueryResult.count + count,
|
|
306
|
+
items: [...shardedQueryResult.items, ...items],
|
|
307
|
+
pageKeys: {
|
|
308
|
+
...shardedQueryResult.pageKeys,
|
|
309
|
+
...((0, _isUndefined2.default)(pageKey) ? {} : {
|
|
310
|
+
[shardedKey]: pageKey
|
|
311
|
+
})
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}, {
|
|
315
|
+
count: 0,
|
|
316
|
+
items: [],
|
|
317
|
+
pageKeys: {}
|
|
318
|
+
});
|
|
319
|
+
result.count += pageResult.count;
|
|
320
|
+
result.items = [...result.items, ...pageResult.items];
|
|
321
|
+
result.pageKeys = pageResult.pageKeys;
|
|
322
|
+
page++;
|
|
323
|
+
} while (page < pages && !(0, _isEmpty2.default)(result.pageKeys));
|
|
309
324
|
return result;
|
|
310
325
|
}
|
|
311
326
|
|
|
@@ -323,7 +338,7 @@ class EntityManager {
|
|
|
323
338
|
rehydrateIndex(entityToken, indexToken) {
|
|
324
339
|
let value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
|
|
325
340
|
let delimiter = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '~';
|
|
326
|
-
const indexComponents = (0, _isArray2.default)(indexToken) ? indexToken :
|
|
341
|
+
const indexComponents = (0, _isArray2.default)(indexToken) ? indexToken : this.#entityManager.getIndexComponents(entityToken, indexToken);
|
|
327
342
|
const indexKeys = (0, _sortBy2.default)(indexComponents.reduce((keys, component) => {
|
|
328
343
|
const {
|
|
329
344
|
encode
|
|
@@ -352,10 +367,10 @@ class EntityManager {
|
|
|
352
367
|
* @throws {Error} If item is invalid.
|
|
353
368
|
*/
|
|
354
369
|
removeKeys(entityToken, item) {
|
|
355
|
-
|
|
370
|
+
this.#entityManager.logger.debug(`removing sharded index keys from ${entityToken}...`, item);
|
|
356
371
|
|
|
357
372
|
// Validate item.
|
|
358
|
-
|
|
373
|
+
this.#entityManager.validateItem(item);
|
|
359
374
|
|
|
360
375
|
// Clone item.
|
|
361
376
|
const newItem = (0, _cloneDeep2.default)(item);
|
|
@@ -363,10 +378,10 @@ class EntityManager {
|
|
|
363
378
|
// Get entity config.
|
|
364
379
|
const {
|
|
365
380
|
shardKeyToken
|
|
366
|
-
} =
|
|
381
|
+
} = this.#entityManager;
|
|
367
382
|
const {
|
|
368
383
|
keys
|
|
369
|
-
} =
|
|
384
|
+
} = this.#entityManager.getEntityConfig(entityToken);
|
|
370
385
|
|
|
371
386
|
// Remove shardKey.
|
|
372
387
|
delete newItem[shardKeyToken];
|
|
@@ -375,7 +390,7 @@ class EntityManager {
|
|
|
375
390
|
(0, _forEach2.default)(keys, (value, key) => {
|
|
376
391
|
if (!value.retain) delete newItem[key];
|
|
377
392
|
});
|
|
378
|
-
|
|
393
|
+
this.#entityManager.logger.debug('done', newItem);
|
|
379
394
|
return newItem;
|
|
380
395
|
}
|
|
381
396
|
}
|
|
@@ -26,13 +26,6 @@ var _mapValues2 = _interopRequireDefault(require("lodash/mapValues"));
|
|
|
26
26
|
var _jsonschema = require("jsonschema");
|
|
27
27
|
var _stringHash = _interopRequireDefault(require("string-hash"));
|
|
28
28
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
29
|
-
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
30
|
-
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
31
|
-
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
|
|
32
|
-
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
|
|
33
|
-
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
|
|
34
|
-
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
|
|
35
|
-
function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } }
|
|
36
29
|
/**
|
|
37
30
|
* EntityManager config validation schema.
|
|
38
31
|
*
|
|
@@ -122,9 +115,10 @@ const keySchema = {
|
|
|
122
115
|
*
|
|
123
116
|
* @private
|
|
124
117
|
*/
|
|
125
|
-
var _config = /*#__PURE__*/new WeakMap();
|
|
126
|
-
var _logger = /*#__PURE__*/new WeakMap();
|
|
127
118
|
class PrivateEntityManager {
|
|
119
|
+
#config;
|
|
120
|
+
#logger;
|
|
121
|
+
|
|
128
122
|
/**
|
|
129
123
|
* Create a PrivateEntityManager instance.
|
|
130
124
|
*
|
|
@@ -139,17 +133,9 @@ class PrivateEntityManager {
|
|
|
139
133
|
config = {},
|
|
140
134
|
logger = console
|
|
141
135
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
142
|
-
_classPrivateFieldInitSpec(this, _config, {
|
|
143
|
-
writable: true,
|
|
144
|
-
value: void 0
|
|
145
|
-
});
|
|
146
|
-
_classPrivateFieldInitSpec(this, _logger, {
|
|
147
|
-
writable: true,
|
|
148
|
-
value: void 0
|
|
149
|
-
});
|
|
150
136
|
// Validate logger.
|
|
151
137
|
if (!logger.error || !logger.debug) throw new Error('logger must implement error & debug methods.');
|
|
152
|
-
|
|
138
|
+
this.#logger = logger;
|
|
153
139
|
this.config = config;
|
|
154
140
|
}
|
|
155
141
|
|
|
@@ -159,7 +145,7 @@ class PrivateEntityManager {
|
|
|
159
145
|
* @returns {object} Current config.
|
|
160
146
|
*/
|
|
161
147
|
get config() {
|
|
162
|
-
return
|
|
148
|
+
return this.#config;
|
|
163
149
|
}
|
|
164
150
|
|
|
165
151
|
/**
|
|
@@ -266,7 +252,7 @@ class PrivateEntityManager {
|
|
|
266
252
|
// Validate sharding timestamp is a function or undefined.
|
|
267
253
|
if (!(0, _isNil2.default)(timestamp) && !(0, _isFunction2.default)(timestamp)) throw new Error(`${entityToken} sharding timestamp must be a function or nil.`);else return false;
|
|
268
254
|
});
|
|
269
|
-
|
|
255
|
+
this.#config = conformedConfig;
|
|
270
256
|
}
|
|
271
257
|
|
|
272
258
|
/**
|
|
@@ -275,7 +261,7 @@ class PrivateEntityManager {
|
|
|
275
261
|
* @returns {object} Logger instance.
|
|
276
262
|
*/
|
|
277
263
|
get logger() {
|
|
278
|
-
return
|
|
264
|
+
return this.#logger;
|
|
279
265
|
}
|
|
280
266
|
|
|
281
267
|
/**
|
|
@@ -284,7 +270,7 @@ class PrivateEntityManager {
|
|
|
284
270
|
* @returns {string} Shard key token.
|
|
285
271
|
*/
|
|
286
272
|
get shardKeyToken() {
|
|
287
|
-
return
|
|
273
|
+
return this.#config.shardKeyToken;
|
|
288
274
|
}
|
|
289
275
|
|
|
290
276
|
/**
|
|
@@ -411,7 +397,7 @@ class PrivateEntityManager {
|
|
|
411
397
|
* Tests whether a timestamp is valid.
|
|
412
398
|
*
|
|
413
399
|
* @param {number} timestamp - timestamp.
|
|
414
|
-
* @param {boolean} [future
|
|
400
|
+
* @param {boolean} [future] - true if timestamp must be in the future.
|
|
415
401
|
* @returns {boolean} true if timestamp is valid.
|
|
416
402
|
*/
|
|
417
403
|
validateTimestamp(timestamp) {
|
|
@@ -34,7 +34,7 @@ export class EntityManager {
|
|
|
34
34
|
*
|
|
35
35
|
* @param {string} entityToken - Entity token.
|
|
36
36
|
* @param {object} item - Entity item.
|
|
37
|
-
* @param {boolean} [overwrite
|
|
37
|
+
* @param {boolean} [overwrite] - Overwrite existing properties.
|
|
38
38
|
* @returns {object} Decorated entity item.
|
|
39
39
|
* @throws {Error} If entityToken is invalid.
|
|
40
40
|
* @throws {Error} If item is invalid.
|
|
@@ -212,11 +212,12 @@ export class EntityManager {
|
|
|
212
212
|
*
|
|
213
213
|
* @param {object} options - Query options.
|
|
214
214
|
* @param {string} options.entityToken - Entity token.
|
|
215
|
-
* @param {string} [options.keyToken
|
|
216
|
-
* @param {object} [options.item
|
|
215
|
+
* @param {string} [options.keyToken] - Key token.
|
|
216
|
+
* @param {object} [options.item] - Entity item sufficiently populated to generate property keyToken.
|
|
217
217
|
* @param {ShardQueryFunction} options.shardQuery - Sharded query function.
|
|
218
218
|
* @param {number} [options.limit] - Request limit.
|
|
219
219
|
* @param {object} [options.pageKeys] - Map of shard page keys.
|
|
220
|
+
* @param {number|'all'} [options.pages] - Max number of pages to query.
|
|
220
221
|
* @returns {Promise<ShardedQueryResult>} Sharded query result.
|
|
221
222
|
*/
|
|
222
223
|
async query({
|
|
@@ -226,16 +227,23 @@ export class EntityManager {
|
|
|
226
227
|
shardQuery,
|
|
227
228
|
limit,
|
|
228
229
|
pageKeys,
|
|
230
|
+
pages = 1,
|
|
229
231
|
} = {}) {
|
|
230
232
|
// Validate params.
|
|
231
233
|
this.#entityManager.validateKeyToken(entityToken, keyToken);
|
|
232
234
|
this.#entityManager.validateItem(item);
|
|
233
235
|
if (!_.isFunction(shardQuery))
|
|
234
236
|
throw new Error('shardQuery must be a function');
|
|
235
|
-
if (!_.isUndefined(limit) && !_.isInteger(limit))
|
|
236
|
-
throw new Error('limit must be
|
|
237
|
+
if (!_.isUndefined(limit) && !(_.isInteger(limit) && limit >= 1))
|
|
238
|
+
throw new Error('limit must be a positive integer');
|
|
237
239
|
if (!_.isUndefined(pageKeys) && !_.isPlainObject(pageKeys))
|
|
238
240
|
throw new Error('pageKeys must be an object');
|
|
241
|
+
if (!_.isUndefined(pages) && !/^(?:\d+|all)$/i.test(pages.toString()))
|
|
242
|
+
throw new Error("pages must be a positive integer or 'all'");
|
|
243
|
+
|
|
244
|
+
// Parse pages.
|
|
245
|
+
if (_.isString(pages))
|
|
246
|
+
pages = pages.toLowerCase() === 'all' ? Infinity : _.parseInt(pages);
|
|
239
247
|
|
|
240
248
|
// Generate default pageKeys if not provided
|
|
241
249
|
pageKeys ??= _.fromPairs(
|
|
@@ -253,32 +261,43 @@ export class EntityManager {
|
|
|
253
261
|
? undefined
|
|
254
262
|
: Math.ceil(limit / _.size(pageKeys));
|
|
255
263
|
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
// Iterate search over pages.
|
|
265
|
+
let page = 0;
|
|
266
|
+
const result = { count: 0, items: [], pageKeys };
|
|
267
|
+
|
|
268
|
+
do {
|
|
269
|
+
// Query every shard in pageKeys.
|
|
270
|
+
const shardQueryResults = await Promise.all(
|
|
271
|
+
_.map(
|
|
272
|
+
result.pageKeys,
|
|
273
|
+
(pageKey, shardedKey) =>
|
|
274
|
+
new Promise((resolve) =>
|
|
275
|
+
shardQuery(shardedKey, pageKey, shardLimit).then(
|
|
276
|
+
({ count, items, pageKey }) =>
|
|
277
|
+
resolve({ count, items, pageKey, shardedKey })
|
|
278
|
+
)
|
|
265
279
|
)
|
|
266
|
-
|
|
267
|
-
)
|
|
268
|
-
);
|
|
280
|
+
)
|
|
281
|
+
);
|
|
269
282
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
283
|
+
// Reduce shardQueryResults into a single result.
|
|
284
|
+
const pageResult = shardQueryResults.reduce(
|
|
285
|
+
(shardedQueryResult, { count, items, pageKey, shardedKey }) => ({
|
|
286
|
+
count: shardedQueryResult.count + count,
|
|
287
|
+
items: [...shardedQueryResult.items, ...items],
|
|
288
|
+
pageKeys: {
|
|
289
|
+
...shardedQueryResult.pageKeys,
|
|
290
|
+
...(_.isUndefined(pageKey) ? {} : { [shardedKey]: pageKey }),
|
|
291
|
+
},
|
|
292
|
+
}),
|
|
293
|
+
{ count: 0, items: [], pageKeys: {} }
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
result.count += pageResult.count;
|
|
297
|
+
result.items = [...result.items, ...pageResult.items];
|
|
298
|
+
result.pageKeys = pageResult.pageKeys;
|
|
299
|
+
page++;
|
|
300
|
+
} while (page < pages && !_.isEmpty(result.pageKeys));
|
|
282
301
|
|
|
283
302
|
return result;
|
|
284
303
|
}
|
|
@@ -408,7 +408,7 @@ export class PrivateEntityManager {
|
|
|
408
408
|
* Tests whether a timestamp is valid.
|
|
409
409
|
*
|
|
410
410
|
* @param {number} timestamp - timestamp.
|
|
411
|
-
* @param {boolean} [future
|
|
411
|
+
* @param {boolean} [future] - true if timestamp must be in the future.
|
|
412
412
|
* @returns {boolean} true if timestamp is valid.
|
|
413
413
|
*/
|
|
414
414
|
validateTimestamp(timestamp, future = false) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karmaniverous/entity-manager",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0-1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -26,27 +26,25 @@
|
|
|
26
26
|
"string-hash": "^1.1.3"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@babel/cli": "^7.
|
|
30
|
-
"@babel/core": "^7.
|
|
31
|
-
"@babel/eslint-parser": "^7.
|
|
32
|
-
"@babel/plugin-syntax-import-assertions": "^7.
|
|
33
|
-
"@babel/preset-env": "^7.
|
|
34
|
-
"@babel/register": "^7.
|
|
35
|
-
"@karmaniverous/edge-logger": "^1.
|
|
36
|
-
"@karmaniverous/
|
|
37
|
-
"@
|
|
38
|
-
"@types/node": "^18.11.18",
|
|
29
|
+
"@babel/cli": "^7.22.5",
|
|
30
|
+
"@babel/core": "^7.22.5",
|
|
31
|
+
"@babel/eslint-parser": "^7.22.5",
|
|
32
|
+
"@babel/plugin-syntax-import-assertions": "^7.22.5",
|
|
33
|
+
"@babel/preset-env": "^7.22.5",
|
|
34
|
+
"@babel/register": "^7.22.5",
|
|
35
|
+
"@karmaniverous/edge-logger": "^1.2.1",
|
|
36
|
+
"@karmaniverous/tagged-templates": "^0.1.1",
|
|
37
|
+
"@types/node": "^20.3.1",
|
|
39
38
|
"babel-plugin-lodash": "^3.3.4",
|
|
40
39
|
"chai": "^4.3.7",
|
|
41
|
-
"concat-md": "^0.5.
|
|
42
|
-
"eslint": "^8.
|
|
43
|
-
"eslint-
|
|
44
|
-
"eslint-plugin-jsdoc": "^39.7.4",
|
|
40
|
+
"concat-md": "^0.5.1",
|
|
41
|
+
"eslint": "^8.43.0",
|
|
42
|
+
"eslint-plugin-jsdoc": "^46.2.6",
|
|
45
43
|
"eslint-plugin-mocha": "^10.1.0",
|
|
44
|
+
"eslint-plugin-promise": "^6.1.1",
|
|
46
45
|
"jsdoc-to-markdown": "^8.0.0",
|
|
47
46
|
"mocha": "^10.2.0",
|
|
48
|
-
"
|
|
49
|
-
"release-it": "^15.6.0"
|
|
47
|
+
"release-it": "^15.11.0"
|
|
50
48
|
},
|
|
51
49
|
"exports": {
|
|
52
50
|
".": {
|
|
@@ -80,8 +78,8 @@
|
|
|
80
78
|
"test": "mocha",
|
|
81
79
|
"build": "babel lib -d dist/default/lib --delete-dir-on-start --config-file ./dist/default/.babelrc",
|
|
82
80
|
"doc": "jsdoc2md -c doc/jsdoc.config.json -f lib/**/*.* -t doc/api-template.hbs > doc/2-api.jsdoc2.md && concat-md doc --hide-anchor-links > README.md",
|
|
83
|
-
"
|
|
84
|
-
"release": "
|
|
81
|
+
"prerelease": "npm run lint && npm run test && npm run build && npm run doc",
|
|
82
|
+
"release": "release-it"
|
|
85
83
|
},
|
|
86
84
|
"type": "module"
|
|
87
85
|
}
|