@karmaniverous/entity-manager 0.0.11 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -38,8 +38,8 @@ The `entity-manager` configuration object describes each entity's structured key
|
|
|
38
38
|
|
|
39
39
|
// These tagged templates are used below to simplify template literals.
|
|
40
40
|
// sn2e - Template literal returns empty string if any expression is nil.
|
|
41
|
-
//
|
|
42
|
-
import { sn2e,
|
|
41
|
+
// sn2u - Template literal returns undefined if any expression is nil.
|
|
42
|
+
import { sn2e, sn2u } from '@karmaniverous/tagged-templates';
|
|
43
43
|
|
|
44
44
|
// Entity config object named export.
|
|
45
45
|
export const config = {
|
|
@@ -57,31 +57,31 @@ export const config = {
|
|
|
57
57
|
|
|
58
58
|
// Table RANGE key.
|
|
59
59
|
entitySK: ({ timestamp, transactionId }) =>
|
|
60
|
-
|
|
60
|
+
sn2u`timestamp#${timestamp}|transactionId#${transactionId}`,
|
|
61
61
|
|
|
62
62
|
// merchants GSI HASH key. Note the optional shardId.
|
|
63
63
|
merchantPK: ({ merchantId, shardId }) =>
|
|
64
|
-
|
|
64
|
+
sn2u`merchantId#${merchantId}|transaction${sn2e`!${shardId}`}`,
|
|
65
65
|
|
|
66
66
|
// merchants GSI RANGE key.
|
|
67
67
|
merchantSK: ({ methodId, timestamp, transactionId }) =>
|
|
68
|
-
|
|
68
|
+
sn2u`timestamp#${timestamp}|methodId#${methodId}|transactionId#${transactionId}`,
|
|
69
69
|
|
|
70
70
|
// methods GSI HASH key. Note the optional shardId.
|
|
71
71
|
methodPK: ({ methodId, shardId }) =>
|
|
72
|
-
|
|
72
|
+
sn2u`method#${methodId}|transaction${sn2e`!${shardId}`}`,
|
|
73
73
|
|
|
74
74
|
// methods GSI RANGE key.
|
|
75
75
|
methodSK: ({ merchantId, timestamp, transactionId }) =>
|
|
76
|
-
|
|
76
|
+
sn2u`timestamp#${timestamp}|merchantId#${merchantId}|transactionId#${transactionId}`,
|
|
77
77
|
|
|
78
78
|
// users GSI HASH key. Note the optional shardId.
|
|
79
79
|
userPK: ({ shardId, userId }) =>
|
|
80
|
-
|
|
80
|
+
sn2u`user#${userId}|transaction${sn2e`!${shardId}`}`,
|
|
81
81
|
|
|
82
82
|
// users GSI RANGE key.
|
|
83
83
|
userSK: ({ merchantId, timestamp, transactionId }) =>
|
|
84
|
-
|
|
84
|
+
sn2u`timestamp#${timestamp}|merchantId#${merchantId}|transactionId#${transactionId}`,
|
|
85
85
|
},
|
|
86
86
|
|
|
87
87
|
// The sharding configuration for this entity.
|
|
@@ -216,10 +216,16 @@ This change can be accomplished with no breaking changes to existing implementat
|
|
|
216
216
|
## entity-manager
|
|
217
217
|
|
|
218
218
|
* [entity-manager](#module_entity-manager)
|
|
219
|
-
*
|
|
220
|
-
* [
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
* _static_
|
|
220
|
+
* [.EntityManager](#module_entity-manager.EntityManager)
|
|
221
|
+
* [new exports.EntityManager(options)](#new_module_entity-manager.EntityManager_new)
|
|
222
|
+
* [.addKeys(entityToken, item, [overwrite])](#module_entity-manager.EntityManager+addKeys) ⇒ <code>object</code>
|
|
223
|
+
* [.getKeySpace(entityToken, keyToken, item, timestamp)](#module_entity-manager.EntityManager+getKeySpace) ⇒ <code>Array.<string></code>
|
|
224
|
+
* [.query(entityToken, keyToken, item, shardQuery, [shardPageKeys])](#module_entity-manager.EntityManager+query) ⇒ <code>Promise.<ShardedQueryResult></code>
|
|
225
|
+
* _inner_
|
|
226
|
+
* [~ShardQueryResult](#module_entity-manager..ShardQueryResult) : <code>object</code>
|
|
227
|
+
* [~ShardQueryFunction](#module_entity-manager..ShardQueryFunction) ⇒ <code>Promise.<ShardQueryResult></code>
|
|
228
|
+
* [~ShardedQueryResult](#module_entity-manager..ShardedQueryResult) : <code>object</code>
|
|
223
229
|
|
|
224
230
|
<a name="module_entity-manager.EntityManager"></a>
|
|
225
231
|
|
|
@@ -231,7 +237,8 @@ Manage DynamoDb entities.
|
|
|
231
237
|
* [.EntityManager](#module_entity-manager.EntityManager)
|
|
232
238
|
* [new exports.EntityManager(options)](#new_module_entity-manager.EntityManager_new)
|
|
233
239
|
* [.addKeys(entityToken, item, [overwrite])](#module_entity-manager.EntityManager+addKeys) ⇒ <code>object</code>
|
|
234
|
-
* [.getKeySpace(entityToken,
|
|
240
|
+
* [.getKeySpace(entityToken, keyToken, item, timestamp)](#module_entity-manager.EntityManager+getKeySpace) ⇒ <code>Array.<string></code>
|
|
241
|
+
* [.query(entityToken, keyToken, item, shardQuery, [shardPageKeys])](#module_entity-manager.EntityManager+query) ⇒ <code>Promise.<ShardedQueryResult></code>
|
|
235
242
|
|
|
236
243
|
<a name="new_module_entity-manager.EntityManager_new"></a>
|
|
237
244
|
|
|
@@ -272,7 +279,7 @@ Decorate an entity item with keys.
|
|
|
272
279
|
|
|
273
280
|
<a name="module_entity-manager.EntityManager+getKeySpace"></a>
|
|
274
281
|
|
|
275
|
-
#### entityManager.getKeySpace(entityToken,
|
|
282
|
+
#### entityManager.getKeySpace(entityToken, keyToken, item, timestamp) ⇒ <code>Array.<string></code>
|
|
276
283
|
Return an array of sharded keys valid for a given entity token & timestamp.
|
|
277
284
|
|
|
278
285
|
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
@@ -288,10 +295,61 @@ Return an array of sharded keys valid for a given entity token & timestamp.
|
|
|
288
295
|
| Param | Type | Description |
|
|
289
296
|
| --- | --- | --- |
|
|
290
297
|
| entityToken | <code>string</code> | Entity token. |
|
|
291
|
-
| item | <code>object</code> | Entity item. |
|
|
292
298
|
| keyToken | <code>string</code> | Key token. |
|
|
299
|
+
| item | <code>object</code> | Entity item sufficiently populated to generate property keyToken. |
|
|
293
300
|
| timestamp | <code>number</code> | Timestamp. |
|
|
294
301
|
|
|
302
|
+
<a name="module_entity-manager.EntityManager+query"></a>
|
|
303
|
+
|
|
304
|
+
#### entityManager.query(entityToken, keyToken, item, shardQuery, [shardPageKeys]) ⇒ <code>Promise.<ShardedQueryResult></code>
|
|
305
|
+
Query an entity across shards.
|
|
306
|
+
|
|
307
|
+
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
308
|
+
**Returns**: <code>Promise.<ShardedQueryResult></code> - Sharded query result.
|
|
309
|
+
|
|
310
|
+
| Param | Type | Description |
|
|
311
|
+
| --- | --- | --- |
|
|
312
|
+
| entityToken | <code>string</code> | Entity token. |
|
|
313
|
+
| keyToken | <code>string</code> | Key token. |
|
|
314
|
+
| item | <code>object</code> | Entity item sufficiently populated to generate property keyToken. |
|
|
315
|
+
| shardQuery | <code>ShardQueryFunction</code> | Sharded query function. |
|
|
316
|
+
| [shardPageKeys] | <code>object</code> | Map of shard page keys. |
|
|
317
|
+
|
|
318
|
+
<a name="module_entity-manager..ShardQueryResult"></a>
|
|
319
|
+
|
|
320
|
+
### entity-manager~ShardQueryResult : <code>object</code>
|
|
321
|
+
**Kind**: inner typedef of [<code>entity-manager</code>](#module_entity-manager)
|
|
322
|
+
**Properties**
|
|
323
|
+
|
|
324
|
+
| Name | Type | Description |
|
|
325
|
+
| --- | --- | --- |
|
|
326
|
+
| items | <code>Array.<any></code> | Query result array. |
|
|
327
|
+
| pageKey | <code>\*</code> | Shard page key. |
|
|
328
|
+
|
|
329
|
+
<a name="module_entity-manager..ShardQueryFunction"></a>
|
|
330
|
+
|
|
331
|
+
### entity-manager~ShardQueryFunction ⇒ <code>Promise.<ShardQueryResult></code>
|
|
332
|
+
Shard query function
|
|
333
|
+
|
|
334
|
+
**Kind**: inner typedef of [<code>entity-manager</code>](#module_entity-manager)
|
|
335
|
+
**Returns**: <code>Promise.<ShardQueryResult></code> - Sharded query result.
|
|
336
|
+
|
|
337
|
+
| Param | Type | Description |
|
|
338
|
+
| --- | --- | --- |
|
|
339
|
+
| shardedKey | <code>string</code> | Sharded key. |
|
|
340
|
+
| [pageKey] | <code>\*</code> | Page key. |
|
|
341
|
+
|
|
342
|
+
<a name="module_entity-manager..ShardedQueryResult"></a>
|
|
343
|
+
|
|
344
|
+
### entity-manager~ShardedQueryResult : <code>object</code>
|
|
345
|
+
**Kind**: inner typedef of [<code>entity-manager</code>](#module_entity-manager)
|
|
346
|
+
**Properties**
|
|
347
|
+
|
|
348
|
+
| Name | Type | Description |
|
|
349
|
+
| --- | --- | --- |
|
|
350
|
+
| items | <code>Array.<any></code> | Query result array. |
|
|
351
|
+
| shardPageKeys | <code>object</code> | Shard page keys. |
|
|
352
|
+
|
|
295
353
|
|
|
296
354
|
---
|
|
297
355
|
|
|
@@ -4,6 +4,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.EntityManager = void 0;
|
|
7
|
+
var _isUndefined2 = _interopRequireDefault(require("lodash/isUndefined"));
|
|
8
|
+
var _map2 = _interopRequireDefault(require("lodash/map"));
|
|
9
|
+
var _fromPairs2 = _interopRequireDefault(require("lodash/fromPairs"));
|
|
7
10
|
var _sortedUniq2 = _interopRequireDefault(require("lodash/sortedUniq"));
|
|
8
11
|
var _forEach2 = _interopRequireDefault(require("lodash/forEach"));
|
|
9
12
|
var _isNil2 = _interopRequireDefault(require("lodash/isNil"));
|
|
@@ -84,6 +87,9 @@ class EntityManager {
|
|
|
84
87
|
if (overwrite || (0, _isNil2.default)(item[key])) item[key] = getValue(item);
|
|
85
88
|
});
|
|
86
89
|
_classPrivateFieldGet(this, _entityManager).logger.debug('done', item);
|
|
90
|
+
|
|
91
|
+
// Remove undefined shardKey.
|
|
92
|
+
if ((0, _isNil2.default)(item[shardKeyToken])) delete item[shardKeyToken];
|
|
87
93
|
return item;
|
|
88
94
|
}
|
|
89
95
|
|
|
@@ -91,8 +97,8 @@ class EntityManager {
|
|
|
91
97
|
* Return an array of sharded keys valid for a given entity token & timestamp.
|
|
92
98
|
*
|
|
93
99
|
* @param {string} entityToken - Entity token.
|
|
94
|
-
* @param {object} item - Entity item.
|
|
95
100
|
* @param {string} keyToken - Key token.
|
|
101
|
+
* @param {object} item - Entity item sufficiently populated to generate property keyToken.
|
|
96
102
|
* @param {number} timestamp - Timestamp.
|
|
97
103
|
* @returns {string[]} Array of keys.
|
|
98
104
|
* @throws {Error} If entityToken is invalid.
|
|
@@ -100,7 +106,7 @@ class EntityManager {
|
|
|
100
106
|
* @throws {Error} If keyToken is invalid.
|
|
101
107
|
* @throws {Error} If timestamp is invalid.
|
|
102
108
|
*/
|
|
103
|
-
getKeySpace(entityToken,
|
|
109
|
+
getKeySpace(entityToken, keyToken, item) {
|
|
104
110
|
let timestamp = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Date.now();
|
|
105
111
|
_classPrivateFieldGet(this, _entityManager).logger.debug(`getting shard key space for ${entityToken} on key '${keyToken}' at timestamp ${timestamp}...`, item);
|
|
106
112
|
const shardKeySpace = _classPrivateFieldGet(this, _entityManager).getShardKeySpace(entityToken, timestamp);
|
|
@@ -111,5 +117,76 @@ class EntityManager {
|
|
|
111
117
|
_classPrivateFieldGet(this, _entityManager).logger.debug('done', result);
|
|
112
118
|
return result;
|
|
113
119
|
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @typedef {object} ShardQueryResult
|
|
123
|
+
* @property {any[]} items - Query result array.
|
|
124
|
+
* @property {*} pageKey - Shard page key.
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Shard query function
|
|
129
|
+
*
|
|
130
|
+
* @callback ShardQueryFunction
|
|
131
|
+
* @param {string} shardedKey - Sharded key.
|
|
132
|
+
* @param {*} [pageKey] - Page key.
|
|
133
|
+
* @returns {Promise<ShardQueryResult>} Sharded query result.
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @typedef {object} ShardedQueryResult
|
|
138
|
+
* @property {any[]} items - Query result array.
|
|
139
|
+
* @property {object} shardPageKeys - Shard page keys.
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Query an entity across shards.
|
|
144
|
+
*
|
|
145
|
+
* @param {string} entityToken - Entity token.
|
|
146
|
+
* @param {string} keyToken - Key token.
|
|
147
|
+
* @param {object} item - Entity item sufficiently populated to generate property keyToken.
|
|
148
|
+
* @param {ShardQueryFunction} shardQuery - Sharded query function.
|
|
149
|
+
* @param {object} [shardPageKeys] - Map of shard page keys.
|
|
150
|
+
* @returns {Promise<ShardedQueryResult>} Sharded query result.
|
|
151
|
+
*/
|
|
152
|
+
async query(entityToken, keyToken, item, shardQuery, shardPageKeys) {
|
|
153
|
+
// Generate default shardPageKeys if not provided
|
|
154
|
+
shardPageKeys ??= (0, _fromPairs2.default)(this.getKeySpace(entityToken, keyToken, item).map(shardedKey => [shardedKey, undefined]));
|
|
155
|
+
|
|
156
|
+
// Query every shard in shardPageKeys.
|
|
157
|
+
const shardQueryResults = await Promise.all((0, _map2.default)(shardPageKeys, (pageKey, shardedKey) => new Promise(resolve => shardQuery(shardedKey, pageKey).then(_ref2 => {
|
|
158
|
+
let {
|
|
159
|
+
items,
|
|
160
|
+
pageKey
|
|
161
|
+
} = _ref2;
|
|
162
|
+
return resolve({
|
|
163
|
+
shardedKey,
|
|
164
|
+
items,
|
|
165
|
+
pageKey
|
|
166
|
+
});
|
|
167
|
+
}))));
|
|
168
|
+
|
|
169
|
+
// Reduce shardQueryResults into a single result.
|
|
170
|
+
const result = shardQueryResults.reduce((shardedQueryResult, _ref3) => {
|
|
171
|
+
let {
|
|
172
|
+
shardedKey,
|
|
173
|
+
items,
|
|
174
|
+
pageKey
|
|
175
|
+
} = _ref3;
|
|
176
|
+
return {
|
|
177
|
+
items: [...shardedQueryResult.items, ...items],
|
|
178
|
+
shardPageKeys: {
|
|
179
|
+
...shardedQueryResult.shardPageKeys,
|
|
180
|
+
...((0, _isUndefined2.default)(pageKey) ? {} : {
|
|
181
|
+
[shardedKey]: pageKey
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}, {
|
|
186
|
+
items: [],
|
|
187
|
+
shardPageKeys: {}
|
|
188
|
+
});
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
114
191
|
}
|
|
115
192
|
exports.EntityManager = EntityManager;
|
|
@@ -68,6 +68,9 @@ export class EntityManager {
|
|
|
68
68
|
});
|
|
69
69
|
this.#entityManager.logger.debug('done', item);
|
|
70
70
|
|
|
71
|
+
// Remove undefined shardKey.
|
|
72
|
+
if (_.isNil(item[shardKeyToken])) delete item[shardKeyToken];
|
|
73
|
+
|
|
71
74
|
return item;
|
|
72
75
|
}
|
|
73
76
|
|
|
@@ -75,8 +78,8 @@ export class EntityManager {
|
|
|
75
78
|
* Return an array of sharded keys valid for a given entity token & timestamp.
|
|
76
79
|
*
|
|
77
80
|
* @param {string} entityToken - Entity token.
|
|
78
|
-
* @param {object} item - Entity item.
|
|
79
81
|
* @param {string} keyToken - Key token.
|
|
82
|
+
* @param {object} item - Entity item sufficiently populated to generate property keyToken.
|
|
80
83
|
* @param {number} timestamp - Timestamp.
|
|
81
84
|
* @returns {string[]} Array of keys.
|
|
82
85
|
* @throws {Error} If entityToken is invalid.
|
|
@@ -84,7 +87,7 @@ export class EntityManager {
|
|
|
84
87
|
* @throws {Error} If keyToken is invalid.
|
|
85
88
|
* @throws {Error} If timestamp is invalid.
|
|
86
89
|
*/
|
|
87
|
-
getKeySpace(entityToken,
|
|
90
|
+
getKeySpace(entityToken, keyToken, item, timestamp = Date.now()) {
|
|
88
91
|
this.#entityManager.logger.debug(
|
|
89
92
|
`getting shard key space for ${entityToken} on key '${keyToken}' at timestamp ${timestamp}...`,
|
|
90
93
|
item
|
|
@@ -110,4 +113,72 @@ export class EntityManager {
|
|
|
110
113
|
|
|
111
114
|
return result;
|
|
112
115
|
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @typedef {object} ShardQueryResult
|
|
119
|
+
* @property {any[]} items - Query result array.
|
|
120
|
+
* @property {*} pageKey - Shard page key.
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Shard query function
|
|
125
|
+
*
|
|
126
|
+
* @callback ShardQueryFunction
|
|
127
|
+
* @param {string} shardedKey - Sharded key.
|
|
128
|
+
* @param {*} [pageKey] - Page key.
|
|
129
|
+
* @returns {Promise<ShardQueryResult>} Sharded query result.
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @typedef {object} ShardedQueryResult
|
|
134
|
+
* @property {any[]} items - Query result array.
|
|
135
|
+
* @property {object} shardPageKeys - Shard page keys.
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Query an entity across shards.
|
|
140
|
+
*
|
|
141
|
+
* @param {string} entityToken - Entity token.
|
|
142
|
+
* @param {string} keyToken - Key token.
|
|
143
|
+
* @param {object} item - Entity item sufficiently populated to generate property keyToken.
|
|
144
|
+
* @param {ShardQueryFunction} shardQuery - Sharded query function.
|
|
145
|
+
* @param {object} [shardPageKeys] - Map of shard page keys.
|
|
146
|
+
* @returns {Promise<ShardedQueryResult>} Sharded query result.
|
|
147
|
+
*/
|
|
148
|
+
async query(entityToken, keyToken, item, shardQuery, shardPageKeys) {
|
|
149
|
+
// Generate default shardPageKeys if not provided
|
|
150
|
+
shardPageKeys ??= _.fromPairs(
|
|
151
|
+
this.getKeySpace(entityToken, keyToken, item).map((shardedKey) => [
|
|
152
|
+
shardedKey,
|
|
153
|
+
undefined,
|
|
154
|
+
])
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
// Query every shard in shardPageKeys.
|
|
158
|
+
const shardQueryResults = await Promise.all(
|
|
159
|
+
_.map(
|
|
160
|
+
shardPageKeys,
|
|
161
|
+
(pageKey, shardedKey) =>
|
|
162
|
+
new Promise((resolve) =>
|
|
163
|
+
shardQuery(shardedKey, pageKey).then(({ items, pageKey }) =>
|
|
164
|
+
resolve({ shardedKey, items, pageKey })
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Reduce shardQueryResults into a single result.
|
|
171
|
+
const result = shardQueryResults.reduce(
|
|
172
|
+
(shardedQueryResult, { shardedKey, items, pageKey }) => ({
|
|
173
|
+
items: [...shardedQueryResult.items, ...items],
|
|
174
|
+
shardPageKeys: {
|
|
175
|
+
...shardedQueryResult.shardPageKeys,
|
|
176
|
+
...(_.isUndefined(pageKey) ? {} : { [shardedKey]: pageKey }),
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
179
|
+
{ items: [], shardPageKeys: {} }
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
113
184
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@karmaniverous/entity-manager",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
"require": [
|
|
64
64
|
"@babel/register"
|
|
65
65
|
],
|
|
66
|
-
"spec": "./**/*.test.!(*.*)"
|
|
66
|
+
"spec": "./**/*.test.!(*.*)",
|
|
67
|
+
"timeout": 60000
|
|
67
68
|
},
|
|
68
69
|
"release-it": {
|
|
69
70
|
"github": {
|