@karmaniverous/entity-manager 2.1.0 → 2.1.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
|
@@ -221,10 +221,10 @@ This change can be accomplished with no breaking changes to existing implementat
|
|
|
221
221
|
* [new exports.EntityManager(options)](#new_module_entity-manager.EntityManager_new)
|
|
222
222
|
* [.addKeys(entityToken, item, [overwrite])](#module_entity-manager.EntityManager+addKeys) ⇒ <code>object</code>
|
|
223
223
|
* [.calcShardKey(entityToken, item)](#module_entity-manager.EntityManager+calcShardKey) ⇒ <code>string</code>
|
|
224
|
-
* [.dehydrateIndex(entityToken, indexToken,
|
|
225
|
-
* [.rehydrateIndex(entityToken, indexToken, value, [delimiter])](#module_entity-manager.EntityManager+rehydrateIndex) ⇒ <code>object</code>
|
|
224
|
+
* [.dehydrateIndex(entityToken, indexToken, index, [delimiter])](#module_entity-manager.EntityManager+dehydrateIndex) ⇒ <code>string</code>
|
|
226
225
|
* [.getKeySpace(entityToken, keyToken, item, timestamp)](#module_entity-manager.EntityManager+getKeySpace) ⇒ <code>Array.<string></code>
|
|
227
226
|
* [.query(entityToken, keyToken, item, shardQuery, [options])](#module_entity-manager.EntityManager+query) ⇒ <code>Promise.<ShardedQueryResult></code>
|
|
227
|
+
* [.rehydrateIndex(entityToken, indexToken, value, [delimiter])](#module_entity-manager.EntityManager+rehydrateIndex) ⇒ <code>object</code>
|
|
228
228
|
* [.removeKeys(entityToken, item)](#module_entity-manager.EntityManager+removeKeys) ⇒ <code>object</code>
|
|
229
229
|
* _inner_
|
|
230
230
|
* [~ShardQueryResult](#module_entity-manager..ShardQueryResult) : <code>object</code>
|
|
@@ -242,10 +242,10 @@ Manage DynamoDb entities.
|
|
|
242
242
|
* [new exports.EntityManager(options)](#new_module_entity-manager.EntityManager_new)
|
|
243
243
|
* [.addKeys(entityToken, item, [overwrite])](#module_entity-manager.EntityManager+addKeys) ⇒ <code>object</code>
|
|
244
244
|
* [.calcShardKey(entityToken, item)](#module_entity-manager.EntityManager+calcShardKey) ⇒ <code>string</code>
|
|
245
|
-
* [.dehydrateIndex(entityToken, indexToken,
|
|
246
|
-
* [.rehydrateIndex(entityToken, indexToken, value, [delimiter])](#module_entity-manager.EntityManager+rehydrateIndex) ⇒ <code>object</code>
|
|
245
|
+
* [.dehydrateIndex(entityToken, indexToken, index, [delimiter])](#module_entity-manager.EntityManager+dehydrateIndex) ⇒ <code>string</code>
|
|
247
246
|
* [.getKeySpace(entityToken, keyToken, item, timestamp)](#module_entity-manager.EntityManager+getKeySpace) ⇒ <code>Array.<string></code>
|
|
248
247
|
* [.query(entityToken, keyToken, item, shardQuery, [options])](#module_entity-manager.EntityManager+query) ⇒ <code>Promise.<ShardedQueryResult></code>
|
|
248
|
+
* [.rehydrateIndex(entityToken, indexToken, value, [delimiter])](#module_entity-manager.EntityManager+rehydrateIndex) ⇒ <code>object</code>
|
|
249
249
|
* [.removeKeys(entityToken, item)](#module_entity-manager.EntityManager+removeKeys) ⇒ <code>object</code>
|
|
250
250
|
|
|
251
251
|
<a name="new_module_entity-manager.EntityManager_new"></a>
|
|
@@ -300,8 +300,8 @@ Calculated the shard key for an entity item.
|
|
|
300
300
|
|
|
301
301
|
<a name="module_entity-manager.EntityManager+dehydrateIndex"></a>
|
|
302
302
|
|
|
303
|
-
#### entityManager.dehydrateIndex(entityToken, indexToken,
|
|
304
|
-
|
|
303
|
+
#### entityManager.dehydrateIndex(entityToken, indexToken, index, [delimiter]) ⇒ <code>string</code>
|
|
304
|
+
Condense an index object into a delimited string.
|
|
305
305
|
|
|
306
306
|
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
307
307
|
**Returns**: <code>string</code> - Dehydrated index.
|
|
@@ -316,27 +316,7 @@ Convert a item into a delimited string containing the properties of a named inde
|
|
|
316
316
|
| --- | --- | --- | --- |
|
|
317
317
|
| entityToken | <code>string</code> | | Entity token. |
|
|
318
318
|
| indexToken | <code>string</code> | | Index token. |
|
|
319
|
-
|
|
|
320
|
-
| [delimiter] | <code>string</code> | <code>"~"</code> | Delimiter. |
|
|
321
|
-
|
|
322
|
-
<a name="module_entity-manager.EntityManager+rehydrateIndex"></a>
|
|
323
|
-
|
|
324
|
-
#### entityManager.rehydrateIndex(entityToken, indexToken, value, [delimiter]) ⇒ <code>object</code>
|
|
325
|
-
Convert a delimited string into a named index key.
|
|
326
|
-
|
|
327
|
-
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
328
|
-
**Returns**: <code>object</code> - Rehydrated index key.
|
|
329
|
-
**Throws**:
|
|
330
|
-
|
|
331
|
-
- <code>Error</code> If entityToken is invalid.
|
|
332
|
-
- <code>Error</code> If indexToken is invalid.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
| Param | Type | Default | Description |
|
|
336
|
-
| --- | --- | --- | --- |
|
|
337
|
-
| entityToken | <code>string</code> | | Entity token. |
|
|
338
|
-
| indexToken | <code>string</code> | | Index token. |
|
|
339
|
-
| value | <code>string</code> | | Dehydrated index value. |
|
|
319
|
+
| index | <code>object</code> | | Index object. |
|
|
340
320
|
| [delimiter] | <code>string</code> | <code>"~"</code> | Delimiter. |
|
|
341
321
|
|
|
342
322
|
<a name="module_entity-manager.EntityManager+getKeySpace"></a>
|
|
@@ -379,6 +359,26 @@ Query an entity across shards.
|
|
|
379
359
|
| [options.limit] | <code>number</code> | Request limit. |
|
|
380
360
|
| [options.pageKeys] | <code>object</code> | Map of shard page keys. |
|
|
381
361
|
|
|
362
|
+
<a name="module_entity-manager.EntityManager+rehydrateIndex"></a>
|
|
363
|
+
|
|
364
|
+
#### entityManager.rehydrateIndex(entityToken, indexToken, value, [delimiter]) ⇒ <code>object</code>
|
|
365
|
+
Convert a delimited string into a named index key.
|
|
366
|
+
|
|
367
|
+
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
368
|
+
**Returns**: <code>object</code> - Rehydrated index key.
|
|
369
|
+
**Throws**:
|
|
370
|
+
|
|
371
|
+
- <code>Error</code> If entityToken is invalid.
|
|
372
|
+
- <code>Error</code> If indexToken is invalid.
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
| Param | Type | Default | Description |
|
|
376
|
+
| --- | --- | --- | --- |
|
|
377
|
+
| entityToken | <code>string</code> | | Entity token. |
|
|
378
|
+
| indexToken | <code>string</code> | | Index token. |
|
|
379
|
+
| value | <code>string</code> | | Dehydrated index value. |
|
|
380
|
+
| [delimiter] | <code>string</code> | <code>"~"</code> | Delimiter. |
|
|
381
|
+
|
|
382
382
|
<a name="module_entity-manager.EntityManager+removeKeys"></a>
|
|
383
383
|
|
|
384
384
|
#### entityManager.removeKeys(entityToken, item) ⇒ <code>object</code>
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.EntityManager = void 0;
|
|
7
|
+
var _zipObject2 = _interopRequireDefault(require("lodash/zipObject"));
|
|
7
8
|
var _map2 = _interopRequireDefault(require("lodash/map"));
|
|
8
9
|
var _size2 = _interopRequireDefault(require("lodash/size"));
|
|
9
10
|
var _isEmpty2 = _interopRequireDefault(require("lodash/isEmpty"));
|
|
@@ -11,14 +12,11 @@ var _fromPairs2 = _interopRequireDefault(require("lodash/fromPairs"));
|
|
|
11
12
|
var _isPlainObject2 = _interopRequireDefault(require("lodash/isPlainObject"));
|
|
12
13
|
var _isInteger2 = _interopRequireDefault(require("lodash/isInteger"));
|
|
13
14
|
var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined"));
|
|
14
|
-
var _isFunction2 = _interopRequireDefault(require("lodash/isFunction"));
|
|
15
15
|
var _sortedUniq2 = _interopRequireDefault(require("lodash/sortedUniq"));
|
|
16
|
-
var
|
|
17
|
-
var _flatten2 = _interopRequireDefault(require("lodash/flatten"));
|
|
18
|
-
var _uniq2 = _interopRequireDefault(require("lodash/uniq"));
|
|
16
|
+
var _entries2 = _interopRequireDefault(require("lodash/entries"));
|
|
19
17
|
var _sortBy2 = _interopRequireDefault(require("lodash/sortBy"));
|
|
20
|
-
var
|
|
21
|
-
var
|
|
18
|
+
var _sortedUniqBy2 = _interopRequireDefault(require("lodash/sortedUniqBy"));
|
|
19
|
+
var _isFunction2 = _interopRequireDefault(require("lodash/isFunction"));
|
|
22
20
|
var _forEach2 = _interopRequireDefault(require("lodash/forEach"));
|
|
23
21
|
var _isNil2 = _interopRequireDefault(require("lodash/isNil"));
|
|
24
22
|
var _cloneDeep2 = _interopRequireDefault(require("lodash/cloneDeep"));
|
|
@@ -33,7 +31,6 @@ function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _
|
|
|
33
31
|
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
32
|
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
33
|
var _entityManager = /*#__PURE__*/new WeakMap();
|
|
36
|
-
var _getIndexProperties = /*#__PURE__*/new WeakMap();
|
|
37
34
|
/**
|
|
38
35
|
* Manage DynamoDb entities.
|
|
39
36
|
*
|
|
@@ -59,10 +56,6 @@ class EntityManager {
|
|
|
59
56
|
writable: true,
|
|
60
57
|
value: void 0
|
|
61
58
|
});
|
|
62
|
-
_classPrivateFieldInitSpec(this, _getIndexProperties, {
|
|
63
|
-
writable: true,
|
|
64
|
-
value: (entityToken, indexComponents) => (0, _sortBy2.default)((0, _uniq2.default)((0, _flatten2.default)(indexComponents.map(keyToken => (0, _inspectParametersDeclaration.getParametersNames)(_classPrivateFieldGet(this, _entityManager).getKeyGenerator(entityToken, keyToken))))))
|
|
65
|
-
});
|
|
66
59
|
_classPrivateFieldSet(this, _entityManager, new _PrivateEntityManager.PrivateEntityManager({
|
|
67
60
|
config,
|
|
68
61
|
logger
|
|
@@ -103,8 +96,11 @@ class EntityManager {
|
|
|
103
96
|
}
|
|
104
97
|
|
|
105
98
|
// Add keys.
|
|
106
|
-
(0, _forEach2.default)(keys, (
|
|
107
|
-
|
|
99
|
+
(0, _forEach2.default)(keys, (_ref2, key) => {
|
|
100
|
+
let {
|
|
101
|
+
encode
|
|
102
|
+
} = _ref2;
|
|
103
|
+
if (overwrite || (0, _isNil2.default)(newItem[key])) newItem[key] = encode(newItem);
|
|
108
104
|
});
|
|
109
105
|
_classPrivateFieldGet(this, _entityManager).logger.debug('done', newItem);
|
|
110
106
|
|
|
@@ -130,52 +126,42 @@ class EntityManager {
|
|
|
130
126
|
}
|
|
131
127
|
|
|
132
128
|
/**
|
|
133
|
-
*
|
|
129
|
+
* Condense an index object into a delimited string.
|
|
134
130
|
*
|
|
135
131
|
* @param {string} entityToken - Entity token.
|
|
136
132
|
* @param {string} indexToken - Index token.
|
|
137
|
-
* @param {object}
|
|
133
|
+
* @param {object} index - Index object.
|
|
138
134
|
* @param {string} [delimiter] - Delimiter.
|
|
139
135
|
* @returns {string} Dehydrated index.
|
|
140
136
|
* @throws {Error} If entityToken is invalid.
|
|
141
137
|
* @throws {Error} If indexToken is invalid.
|
|
142
138
|
* @throws {Error} If item is invalid.
|
|
143
139
|
*/
|
|
144
|
-
dehydrateIndex(entityToken, indexToken,
|
|
140
|
+
dehydrateIndex(entityToken, indexToken, index) {
|
|
145
141
|
let delimiter = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '~';
|
|
146
142
|
// Validate item.
|
|
147
|
-
_classPrivateFieldGet(this, _entityManager).validateItem(
|
|
143
|
+
_classPrivateFieldGet(this, _entityManager).validateItem(index);
|
|
148
144
|
const indexComponents = _classPrivateFieldGet(this, _entityManager).getIndexComponents(entityToken, indexToken);
|
|
149
|
-
const indexProperties =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
rehydrateIndex(entityToken, indexToken) {
|
|
170
|
-
let value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
|
|
171
|
-
let delimiter = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '~';
|
|
172
|
-
const indexComponents = _classPrivateFieldGet(this, _entityManager).getIndexComponents(entityToken, indexToken);
|
|
173
|
-
const indexProperties = _classPrivateFieldGet(this, _getIndexProperties).call(this, entityToken, indexComponents);
|
|
174
|
-
const item = (0, _zipObject2.default)(indexProperties, value.split(delimiter));
|
|
175
|
-
return indexComponents.reduce((index, component) => ({
|
|
176
|
-
...index,
|
|
177
|
-
[component]: _classPrivateFieldGet(this, _entityManager).getKeyGenerator(entityToken, component)(item)
|
|
178
|
-
}), {});
|
|
145
|
+
const indexProperties = indexComponents.reduce((properties, component) => {
|
|
146
|
+
const {
|
|
147
|
+
decode
|
|
148
|
+
} = _classPrivateFieldGet(this, _entityManager).getKey(entityToken, component);
|
|
149
|
+
if (!(0, _isFunction2.default)(decode)) throw new Error(`no decode function defined on ${entityToken} entity key '${component}'`);
|
|
150
|
+
return {
|
|
151
|
+
...properties,
|
|
152
|
+
...decode(index[component])
|
|
153
|
+
};
|
|
154
|
+
}, {});
|
|
155
|
+
return (0, _sortedUniqBy2.default)((0, _sortBy2.default)((0, _entries2.default)(indexProperties), _ref3 => {
|
|
156
|
+
let [key] = _ref3;
|
|
157
|
+
return key;
|
|
158
|
+
}), _ref4 => {
|
|
159
|
+
let [key] = _ref4;
|
|
160
|
+
return key;
|
|
161
|
+
}).map(_ref5 => {
|
|
162
|
+
let [, value] = _ref5;
|
|
163
|
+
return value;
|
|
164
|
+
}).join(delimiter);
|
|
179
165
|
}
|
|
180
166
|
|
|
181
167
|
/**
|
|
@@ -195,7 +181,7 @@ class EntityManager {
|
|
|
195
181
|
let timestamp = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Date.now();
|
|
196
182
|
_classPrivateFieldGet(this, _entityManager).logger.debug(`getting shard key space for ${entityToken} on key '${keyToken}' at timestamp ${timestamp}...`, item);
|
|
197
183
|
const shardKeySpace = _classPrivateFieldGet(this, _entityManager).getShardKeySpace(entityToken, timestamp);
|
|
198
|
-
const result = (0, _sortedUniq2.default)(shardKeySpace.map(shardKey => _classPrivateFieldGet(this, _entityManager).
|
|
184
|
+
const result = (0, _sortedUniq2.default)(shardKeySpace.map(shardKey => _classPrivateFieldGet(this, _entityManager).getKey(entityToken, keyToken).encode({
|
|
199
185
|
...item,
|
|
200
186
|
[_classPrivateFieldGet(this, _entityManager).shardKeyToken]: shardKey
|
|
201
187
|
})));
|
|
@@ -266,11 +252,11 @@ class EntityManager {
|
|
|
266
252
|
const shardQueryResults = await Promise.all((0, _map2.default)(pageKeys, (pageKey, shardedKey) => new Promise(resolve => shardQuery(shardedKey, {
|
|
267
253
|
pageKey,
|
|
268
254
|
limit: shardLimit
|
|
269
|
-
}).then(
|
|
255
|
+
}).then(_ref6 => {
|
|
270
256
|
let {
|
|
271
257
|
items,
|
|
272
258
|
pageKey
|
|
273
|
-
} =
|
|
259
|
+
} = _ref6;
|
|
274
260
|
return resolve({
|
|
275
261
|
shardedKey,
|
|
276
262
|
items,
|
|
@@ -279,12 +265,12 @@ class EntityManager {
|
|
|
279
265
|
}))));
|
|
280
266
|
|
|
281
267
|
// Reduce shardQueryResults into a single result.
|
|
282
|
-
const result = shardQueryResults.reduce((shardedQueryResult,
|
|
268
|
+
const result = shardQueryResults.reduce((shardedQueryResult, _ref7) => {
|
|
283
269
|
let {
|
|
284
270
|
shardedKey,
|
|
285
271
|
items,
|
|
286
272
|
pageKey
|
|
287
|
-
} =
|
|
273
|
+
} = _ref7;
|
|
288
274
|
return {
|
|
289
275
|
items: [...shardedQueryResult.items, ...items],
|
|
290
276
|
pageKeys: {
|
|
@@ -301,6 +287,39 @@ class EntityManager {
|
|
|
301
287
|
return result;
|
|
302
288
|
}
|
|
303
289
|
|
|
290
|
+
/**
|
|
291
|
+
* Convert a delimited string into a named index key.
|
|
292
|
+
*
|
|
293
|
+
* @param {string} entityToken - Entity token.
|
|
294
|
+
* @param {string} indexToken - Index token.
|
|
295
|
+
* @param {string} value - Dehydrated index value.
|
|
296
|
+
* @param {string} [delimiter] - Delimiter.
|
|
297
|
+
* @returns {object} Rehydrated index key.
|
|
298
|
+
* @throws {Error} If entityToken is invalid.
|
|
299
|
+
* @throws {Error} If indexToken is invalid.
|
|
300
|
+
*/
|
|
301
|
+
rehydrateIndex(entityToken, indexToken) {
|
|
302
|
+
let value = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
|
|
303
|
+
let delimiter = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '~';
|
|
304
|
+
const indexComponents = _classPrivateFieldGet(this, _entityManager).getIndexComponents(entityToken, indexToken);
|
|
305
|
+
const indexKeys = (0, _sortBy2.default)(indexComponents.reduce((keys, component) => {
|
|
306
|
+
const {
|
|
307
|
+
encode
|
|
308
|
+
} = _classPrivateFieldGet(this, _entityManager).getKey(entityToken, component);
|
|
309
|
+
return [...new Set([...keys, ...(0, _inspectParametersDeclaration.getParametersNames)(encode)]).values()];
|
|
310
|
+
}, []));
|
|
311
|
+
const indexProperies = (0, _zipObject2.default)(indexKeys, value.split(delimiter));
|
|
312
|
+
return indexComponents.reduce((index, component) => {
|
|
313
|
+
const {
|
|
314
|
+
encode
|
|
315
|
+
} = _classPrivateFieldGet(this, _entityManager).getKey(entityToken, component);
|
|
316
|
+
return {
|
|
317
|
+
...index,
|
|
318
|
+
[component]: encode(indexProperies)
|
|
319
|
+
};
|
|
320
|
+
}, {});
|
|
321
|
+
}
|
|
322
|
+
|
|
304
323
|
/**
|
|
305
324
|
* Remove sharded keys from an entity item. Does not mutate original item.
|
|
306
325
|
*
|
|
@@ -12,8 +12,10 @@ var _filter2 = _interopRequireDefault(require("lodash/filter"));
|
|
|
12
12
|
var _findLast2 = _interopRequireDefault(require("lodash/findLast"));
|
|
13
13
|
var _has2 = _interopRequireDefault(require("lodash/has"));
|
|
14
14
|
var _map2 = _interopRequireDefault(require("lodash/map"));
|
|
15
|
-
var _isFunction2 = _interopRequireDefault(require("lodash/isFunction"));
|
|
16
15
|
var _isNil2 = _interopRequireDefault(require("lodash/isNil"));
|
|
16
|
+
var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined"));
|
|
17
|
+
var _isFunction2 = _interopRequireDefault(require("lodash/isFunction"));
|
|
18
|
+
var _forEach2 = _interopRequireDefault(require("lodash/forEach"));
|
|
17
19
|
var _keys2 = _interopRequireDefault(require("lodash/keys"));
|
|
18
20
|
var _difference2 = _interopRequireDefault(require("lodash/difference"));
|
|
19
21
|
var _some2 = _interopRequireDefault(require("lodash/some"));
|
|
@@ -102,6 +104,15 @@ const configSchema = {
|
|
|
102
104
|
},
|
|
103
105
|
additionalProperties: false
|
|
104
106
|
};
|
|
107
|
+
const keySchema = {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
encode: {},
|
|
111
|
+
decode: {}
|
|
112
|
+
},
|
|
113
|
+
additionalProperties: false,
|
|
114
|
+
required: ['encode']
|
|
115
|
+
};
|
|
105
116
|
|
|
106
117
|
/**
|
|
107
118
|
* Private EntityManager implementation.
|
|
@@ -197,7 +208,7 @@ class PrivateEntityManager {
|
|
|
197
208
|
shardKeyToken
|
|
198
209
|
};
|
|
199
210
|
|
|
200
|
-
//
|
|
211
|
+
// Conform & validate entity properties.
|
|
201
212
|
(0, _some2.default)(conformedConfig.entities, (_ref2, entityToken) => {
|
|
202
213
|
let {
|
|
203
214
|
indexes,
|
|
@@ -220,13 +231,26 @@ class PrivateEntityManager {
|
|
|
220
231
|
} else return false;
|
|
221
232
|
});
|
|
222
233
|
|
|
223
|
-
//
|
|
224
|
-
(0,
|
|
225
|
-
|
|
226
|
-
|
|
234
|
+
// Conform & validate entity keys.
|
|
235
|
+
(0, _forEach2.default)(keys, (value, key) => {
|
|
236
|
+
keys[key] = (0, _isFunction2.default)(value) ? {
|
|
237
|
+
encode: value
|
|
238
|
+
} : value;
|
|
239
|
+
|
|
240
|
+
// Validate key against schema.
|
|
241
|
+
const validatorResult = (0, _jsonschema.validate)(keys[key], keySchema);
|
|
242
|
+
if (!validatorResult.valid) {
|
|
243
|
+
validatorResult.errors.forEach(error => this.logger.error(error.message));
|
|
244
|
+
throw new Error(validatorResult.errors);
|
|
245
|
+
}
|
|
246
|
+
const {
|
|
247
|
+
decode
|
|
248
|
+
} = keys[key];
|
|
249
|
+
if (!(0, _isUndefined2.default)(decode) && !(0, _isFunction2.default)(decode)) {
|
|
250
|
+
const message = `entity ${entityKey} key '${key}' decode must be a function or undefined.`;
|
|
227
251
|
this.logger.error(message);
|
|
228
252
|
throw new Error(message);
|
|
229
|
-
}
|
|
253
|
+
}
|
|
230
254
|
});
|
|
231
255
|
|
|
232
256
|
// Validate sharding bump values increase monotonically with keys.
|
|
@@ -320,7 +344,7 @@ class PrivateEntityManager {
|
|
|
320
344
|
}
|
|
321
345
|
return indexes[indexToken];
|
|
322
346
|
}
|
|
323
|
-
|
|
347
|
+
getKey(entityToken, keyToken) {
|
|
324
348
|
const {
|
|
325
349
|
keys
|
|
326
350
|
} = this.getEntityConfig(entityToken);
|
|
@@ -416,30 +440,30 @@ class PrivateEntityManager {
|
|
|
416
440
|
}
|
|
417
441
|
|
|
418
442
|
/**
|
|
419
|
-
* Tests whether
|
|
443
|
+
* Tests whether an item is valid.
|
|
420
444
|
*
|
|
421
|
-
* @param {string}
|
|
422
|
-
* @
|
|
423
|
-
* @returns {boolean} true if keyToken is valid.
|
|
445
|
+
* @param {string} item - Entity token.
|
|
446
|
+
* @returns {boolean} true if entityToken is valid.
|
|
424
447
|
*/
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const message = `Invalid entity ${entityToken} keyToken: ${keyToken}`;
|
|
448
|
+
validateItem(item) {
|
|
449
|
+
if (!(0, _isPlainObject2.default)(item)) {
|
|
450
|
+
const message = `Invalid item: ${item}`;
|
|
429
451
|
this.logger.error(message);
|
|
430
452
|
throw new Error(message);
|
|
431
453
|
} else return true;
|
|
432
454
|
}
|
|
433
455
|
|
|
434
456
|
/**
|
|
435
|
-
* Tests whether
|
|
457
|
+
* Tests whether a key token is valid.
|
|
436
458
|
*
|
|
437
|
-
* @param {string}
|
|
438
|
-
* @
|
|
459
|
+
* @param {string} entityToken - Entity token.
|
|
460
|
+
* @param {string} keyToken - Key token.
|
|
461
|
+
* @returns {boolean} true if keyToken is valid.
|
|
439
462
|
*/
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
463
|
+
validateKeyToken(entityToken, keyToken) {
|
|
464
|
+
this.validateEntityToken(entityToken);
|
|
465
|
+
if (!this.config.entities[entityToken].keys[keyToken]) {
|
|
466
|
+
const message = `Invalid entity ${entityToken} keyToken: ${keyToken}`;
|
|
443
467
|
this.logger.error(message);
|
|
444
468
|
throw new Error(message);
|
|
445
469
|
} else return true;
|
|
@@ -63,8 +63,8 @@ export class EntityManager {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// Add keys.
|
|
66
|
-
_.forEach(keys, (
|
|
67
|
-
if (overwrite || _.isNil(newItem[key])) newItem[key] =
|
|
66
|
+
_.forEach(keys, ({ encode }, key) => {
|
|
67
|
+
if (overwrite || _.isNil(newItem[key])) newItem[key] = encode(newItem);
|
|
68
68
|
});
|
|
69
69
|
this.#entityManager.logger.debug('done', newItem);
|
|
70
70
|
|
|
@@ -90,90 +90,42 @@ export class EntityManager {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* Condense an index object into a delimited string.
|
|
94
94
|
*
|
|
95
95
|
* @param {string} entityToken - Entity token.
|
|
96
96
|
* @param {string} indexToken - Index token.
|
|
97
|
-
* @param {object}
|
|
97
|
+
* @param {object} index - Index object.
|
|
98
98
|
* @param {string} [delimiter] - Delimiter.
|
|
99
99
|
* @returns {string} Dehydrated index.
|
|
100
100
|
* @throws {Error} If entityToken is invalid.
|
|
101
101
|
* @throws {Error} If indexToken is invalid.
|
|
102
102
|
* @throws {Error} If item is invalid.
|
|
103
103
|
*/
|
|
104
|
-
dehydrateIndex(entityToken, indexToken,
|
|
104
|
+
dehydrateIndex(entityToken, indexToken, index, delimiter = '~') {
|
|
105
105
|
// Validate item.
|
|
106
|
-
this.#entityManager.validateItem(
|
|
107
|
-
|
|
108
|
-
const indexComponents = this.#entityManager.getIndexComponents(
|
|
109
|
-
entityToken,
|
|
110
|
-
indexToken
|
|
111
|
-
);
|
|
106
|
+
this.#entityManager.validateItem(index);
|
|
112
107
|
|
|
113
|
-
const indexProperties = this.#getIndexProperties(
|
|
114
|
-
entityToken,
|
|
115
|
-
indexComponents
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
const { shardKeyToken } = this.#entityManager;
|
|
119
|
-
return _.values(
|
|
120
|
-
_.pick(
|
|
121
|
-
{
|
|
122
|
-
...item,
|
|
123
|
-
[shardKeyToken]: this.calcShardKey(entityToken, item),
|
|
124
|
-
},
|
|
125
|
-
indexProperties
|
|
126
|
-
)
|
|
127
|
-
).join(delimiter);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
#getIndexProperties = (entityToken, indexComponents) =>
|
|
131
|
-
_.sortBy(
|
|
132
|
-
_.uniq(
|
|
133
|
-
_.flatten(
|
|
134
|
-
indexComponents.map((keyToken) =>
|
|
135
|
-
getParametersNames(
|
|
136
|
-
this.#entityManager.getKeyGenerator(entityToken, keyToken)
|
|
137
|
-
)
|
|
138
|
-
)
|
|
139
|
-
)
|
|
140
|
-
)
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Convert a delimited string into a named index key.
|
|
145
|
-
*
|
|
146
|
-
* @param {string} entityToken - Entity token.
|
|
147
|
-
* @param {string} indexToken - Index token.
|
|
148
|
-
* @param {string} value - Dehydrated index value.
|
|
149
|
-
* @param {string} [delimiter] - Delimiter.
|
|
150
|
-
* @returns {object} Rehydrated index key.
|
|
151
|
-
* @throws {Error} If entityToken is invalid.
|
|
152
|
-
* @throws {Error} If indexToken is invalid.
|
|
153
|
-
*/
|
|
154
|
-
rehydrateIndex(entityToken, indexToken, value = '', delimiter = '~') {
|
|
155
108
|
const indexComponents = this.#entityManager.getIndexComponents(
|
|
156
109
|
entityToken,
|
|
157
110
|
indexToken
|
|
158
111
|
);
|
|
159
112
|
|
|
160
|
-
const indexProperties =
|
|
161
|
-
entityToken,
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
);
|
|
113
|
+
const indexProperties = indexComponents.reduce((properties, component) => {
|
|
114
|
+
const { decode } = this.#entityManager.getKey(entityToken, component);
|
|
115
|
+
if (!_.isFunction(decode))
|
|
116
|
+
throw new Error(
|
|
117
|
+
`no decode function defined on ${entityToken} entity key '${component}'`
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return { ...properties, ...decode(index[component]) };
|
|
121
|
+
}, {});
|
|
122
|
+
|
|
123
|
+
return _.sortedUniqBy(
|
|
124
|
+
_.sortBy(_.entries(indexProperties), ([key]) => key),
|
|
125
|
+
([key]) => key
|
|
126
|
+
)
|
|
127
|
+
.map(([, value]) => value)
|
|
128
|
+
.join(delimiter);
|
|
177
129
|
}
|
|
178
130
|
|
|
179
131
|
/**
|
|
@@ -202,10 +154,7 @@ export class EntityManager {
|
|
|
202
154
|
|
|
203
155
|
const result = _.sortedUniq(
|
|
204
156
|
shardKeySpace.map((shardKey) =>
|
|
205
|
-
this.#entityManager.
|
|
206
|
-
entityToken,
|
|
207
|
-
keyToken
|
|
208
|
-
)({
|
|
157
|
+
this.#entityManager.getKey(entityToken, keyToken).encode({
|
|
209
158
|
...item,
|
|
210
159
|
[this.#entityManager.shardKeyToken]: shardKey,
|
|
211
160
|
})
|
|
@@ -312,6 +261,38 @@ export class EntityManager {
|
|
|
312
261
|
return result;
|
|
313
262
|
}
|
|
314
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Convert a delimited string into a named index key.
|
|
266
|
+
*
|
|
267
|
+
* @param {string} entityToken - Entity token.
|
|
268
|
+
* @param {string} indexToken - Index token.
|
|
269
|
+
* @param {string} value - Dehydrated index value.
|
|
270
|
+
* @param {string} [delimiter] - Delimiter.
|
|
271
|
+
* @returns {object} Rehydrated index key.
|
|
272
|
+
* @throws {Error} If entityToken is invalid.
|
|
273
|
+
* @throws {Error} If indexToken is invalid.
|
|
274
|
+
*/
|
|
275
|
+
rehydrateIndex(entityToken, indexToken, value = '', delimiter = '~') {
|
|
276
|
+
const indexComponents = this.#entityManager.getIndexComponents(
|
|
277
|
+
entityToken,
|
|
278
|
+
indexToken
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const indexKeys = _.sortBy(
|
|
282
|
+
indexComponents.reduce((keys, component) => {
|
|
283
|
+
const { encode } = this.#entityManager.getKey(entityToken, component);
|
|
284
|
+
return [...new Set([...keys, ...getParametersNames(encode)]).values()];
|
|
285
|
+
}, [])
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
const indexProperies = _.zipObject(indexKeys, value.split(delimiter));
|
|
289
|
+
|
|
290
|
+
return indexComponents.reduce((index, component) => {
|
|
291
|
+
const { encode } = this.#entityManager.getKey(entityToken, component);
|
|
292
|
+
return { ...index, [component]: encode(indexProperies) };
|
|
293
|
+
}, {});
|
|
294
|
+
}
|
|
295
|
+
|
|
315
296
|
/**
|
|
316
297
|
* Remove sharded keys from an entity item. Does not mutate original item.
|
|
317
298
|
*
|
|
@@ -67,6 +67,16 @@ const configSchema = {
|
|
|
67
67
|
additionalProperties: false,
|
|
68
68
|
};
|
|
69
69
|
|
|
70
|
+
const keySchema = {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
encode: {},
|
|
74
|
+
decode: {},
|
|
75
|
+
},
|
|
76
|
+
additionalProperties: false,
|
|
77
|
+
required: ['encode'],
|
|
78
|
+
};
|
|
79
|
+
|
|
70
80
|
/**
|
|
71
81
|
* Private EntityManager implementation.
|
|
72
82
|
*
|
|
@@ -151,7 +161,7 @@ export class PrivateEntityManager {
|
|
|
151
161
|
shardKeyToken,
|
|
152
162
|
};
|
|
153
163
|
|
|
154
|
-
//
|
|
164
|
+
// Conform & validate entity properties.
|
|
155
165
|
_.some(
|
|
156
166
|
conformedConfig.entities,
|
|
157
167
|
(
|
|
@@ -172,13 +182,25 @@ export class PrivateEntityManager {
|
|
|
172
182
|
} else return false;
|
|
173
183
|
});
|
|
174
184
|
|
|
175
|
-
//
|
|
176
|
-
_.
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
// Conform & validate entity keys.
|
|
186
|
+
_.forEach(keys, (value, key) => {
|
|
187
|
+
keys[key] = _.isFunction(value) ? { encode: value } : value;
|
|
188
|
+
|
|
189
|
+
// Validate key against schema.
|
|
190
|
+
const validatorResult = validate(keys[key], keySchema);
|
|
191
|
+
if (!validatorResult.valid) {
|
|
192
|
+
validatorResult.errors.forEach((error) =>
|
|
193
|
+
this.logger.error(error.message)
|
|
194
|
+
);
|
|
195
|
+
throw new Error(validatorResult.errors);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const { decode } = keys[key];
|
|
199
|
+
if (!_.isUndefined(decode) && !_.isFunction(decode)) {
|
|
200
|
+
const message = `entity ${entityKey} key '${key}' decode must be a function or undefined.`;
|
|
179
201
|
this.logger.error(message);
|
|
180
202
|
throw new Error(message);
|
|
181
|
-
}
|
|
203
|
+
}
|
|
182
204
|
});
|
|
183
205
|
|
|
184
206
|
// Validate sharding bump values increase monotonically with keys.
|
|
@@ -275,7 +297,7 @@ export class PrivateEntityManager {
|
|
|
275
297
|
return indexes[indexToken];
|
|
276
298
|
}
|
|
277
299
|
|
|
278
|
-
|
|
300
|
+
getKey(entityToken, keyToken) {
|
|
279
301
|
const { keys } = this.getEntityConfig(entityToken);
|
|
280
302
|
|
|
281
303
|
if (!_.has(keys, keyToken)) {
|
|
@@ -383,6 +405,20 @@ export class PrivateEntityManager {
|
|
|
383
405
|
} else return true;
|
|
384
406
|
}
|
|
385
407
|
|
|
408
|
+
/**
|
|
409
|
+
* Tests whether an item is valid.
|
|
410
|
+
*
|
|
411
|
+
* @param {string} item - Entity token.
|
|
412
|
+
* @returns {boolean} true if entityToken is valid.
|
|
413
|
+
*/
|
|
414
|
+
validateItem(item) {
|
|
415
|
+
if (!_.isPlainObject(item)) {
|
|
416
|
+
const message = `Invalid item: ${item}`;
|
|
417
|
+
this.logger.error(message);
|
|
418
|
+
throw new Error(message);
|
|
419
|
+
} else return true;
|
|
420
|
+
}
|
|
421
|
+
|
|
386
422
|
/**
|
|
387
423
|
* Tests whether a key token is valid.
|
|
388
424
|
*
|
|
@@ -400,20 +436,6 @@ export class PrivateEntityManager {
|
|
|
400
436
|
} else return true;
|
|
401
437
|
}
|
|
402
438
|
|
|
403
|
-
/**
|
|
404
|
-
* Tests whether an item is valid.
|
|
405
|
-
*
|
|
406
|
-
* @param {string} item - Entity token.
|
|
407
|
-
* @returns {boolean} true if entityToken is valid.
|
|
408
|
-
*/
|
|
409
|
-
validateItem(item) {
|
|
410
|
-
if (!_.isPlainObject(item)) {
|
|
411
|
-
const message = `Invalid item: ${item}`;
|
|
412
|
-
this.logger.error(message);
|
|
413
|
-
throw new Error(message);
|
|
414
|
-
} else return true;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
439
|
/**
|
|
418
440
|
* Tests whether a timestamp is valid.
|
|
419
441
|
*
|