@karmaniverous/entity-manager 0.0.8
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/.env +1 -0
- package/README.md +298 -0
- package/dist/default/lib/EntityManager/EntityManager.js +115 -0
- package/dist/default/lib/EntityManager/PrivateEntityManager.js +408 -0
- package/dist/default/lib/index.js +12 -0
- package/dist/package.json +3 -0
- package/lib/EntityManager/EntityManager.js +113 -0
- package/lib/EntityManager/PrivateEntityManager.js +381 -0
- package/lib/index.js +1 -0
- package/package.json +85 -0
package/.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Load with getdotenv: https://github.com/karmaniverous/get-dotenv
|
package/README.md
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# entity-manager
|
|
2
|
+
|
|
3
|
+
The DynamoDB [single-table design pattern](https://aws.amazon.com/blogs/compute/creating-a-single-table-design-with-amazon-dynamodb/) requires highly-structured key attributes that...
|
|
4
|
+
|
|
5
|
+
- Serve as Global Secondary Index (GSI) keys supporting entity-specific queries.
|
|
6
|
+
- Support sharding across multiple partitions.
|
|
7
|
+
|
|
8
|
+
Entity relationships in a traditional RDBMS are expressed in a set of Foreign Key constraints that map straightforwardly into an entity-relationship diagram (ERD). Keeping implementation and design in sync is painless, and scaling is a matter of hardware and server configuration.
|
|
9
|
+
|
|
10
|
+
All of this takes place at design time. At run time, database structure is more or less fixed.
|
|
11
|
+
|
|
12
|
+
GSIs on a NoSQL platform like DynamoDB are declared as a matter of design-time configuration. Everything ELSE happens at run time, encoded into those critical key attributes at every data write.
|
|
13
|
+
|
|
14
|
+
The logic to accomplish this can be both complex and fragmented across the implementation. Unlike a set of RDBMS foreign key constraints that map one-for-one to the lines in an ERD, the structure of this logic is difficult to visualize and rarely collected in one place.
|
|
15
|
+
|
|
16
|
+
This package shifts the implementation of DynamoDB structure & scaling from logic to configuration. It features:
|
|
17
|
+
|
|
18
|
+
- A simple, declarative configuration format that permits articulation of every index key for every entity, all in one place.
|
|
19
|
+
|
|
20
|
+
- A rational approach to partition sharding that can be encoded directly into table & GSI hash keys and permits scaling over time.
|
|
21
|
+
|
|
22
|
+
- High-performance decoration of entity-specific data objects with configured, shard-aware index values.
|
|
23
|
+
|
|
24
|
+
- High-performance transformation of an entity-specific data object into a key space permitting query of related objects across all partition shards.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install @karmaniverous/entity-manager
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Configuration
|
|
33
|
+
|
|
34
|
+
The `entity-manager` configuration object describes each entity's structured keys and sharding strategy. For broadest compatibility, express this object as a named export from an ES6 module like this:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
// entityConfig.js
|
|
38
|
+
|
|
39
|
+
// These tagged templates are used below to simplify template literals.
|
|
40
|
+
// sn2e - Template literal returns empty string if any expression is nil.
|
|
41
|
+
// sn2u - Template literal returns undefined if any expression is nil.
|
|
42
|
+
import { sn2e, sn2u } from '@karmaniverous/tagged-templates';
|
|
43
|
+
|
|
44
|
+
// Entity config object named export.
|
|
45
|
+
export const config = {
|
|
46
|
+
entities: {
|
|
47
|
+
// Repeat this structure for each entity type. Valid keys match /\w+/
|
|
48
|
+
transaction: {
|
|
49
|
+
// Each property of the keys object defines a structured index key for
|
|
50
|
+
// this entity. Each property value is a function expects an entity
|
|
51
|
+
// object as its one argument. In the examples below, entity attributes
|
|
52
|
+
// are destructured in the function declaration and the return value is
|
|
53
|
+
// expressed as a template literal.
|
|
54
|
+
keys: {
|
|
55
|
+
// Table HASH key. Note the optional shardId.
|
|
56
|
+
entityPK: ({ shardId }) => `transaction${sn2e`!${shardId}`}`,
|
|
57
|
+
|
|
58
|
+
// Table RANGE key.
|
|
59
|
+
entitySK: ({ timestamp, transactionId }) =>
|
|
60
|
+
sn2u`timestamp#${timestamp}|transactionId#${transactionId}`,
|
|
61
|
+
|
|
62
|
+
// merchants GSI HASH key. Note the optional shardId.
|
|
63
|
+
merchantPK: ({ merchantId, shardId }) =>
|
|
64
|
+
sn2u`merchantId#${merchantId}|transaction${sn2e`!${shardId}`}`,
|
|
65
|
+
|
|
66
|
+
// merchants GSI RANGE key.
|
|
67
|
+
merchantSK: ({ methodId, timestamp, transactionId }) =>
|
|
68
|
+
sn2u`timestamp#${timestamp}|methodId#${methodId}|transactionId#${transactionId}`,
|
|
69
|
+
|
|
70
|
+
// methods GSI HASH key. Note the optional shardId.
|
|
71
|
+
methodPK: ({ methodId, shardId }) =>
|
|
72
|
+
sn2u`method#${methodId}|transaction${sn2e`!${shardId}`}`,
|
|
73
|
+
|
|
74
|
+
// methods GSI RANGE key.
|
|
75
|
+
methodSK: ({ merchantId, timestamp, transactionId }) =>
|
|
76
|
+
sn2u`timestamp#${timestamp}|merchantId#${merchantId}|transactionId#${transactionId}`,
|
|
77
|
+
|
|
78
|
+
// users GSI HASH key. Note the optional shardId.
|
|
79
|
+
userPK: ({ shardId, userId }) =>
|
|
80
|
+
sn2u`user#${userId}|transaction${sn2e`!${shardId}`}`,
|
|
81
|
+
|
|
82
|
+
// users GSI RANGE key.
|
|
83
|
+
userSK: ({ merchantId, timestamp, transactionId }) =>
|
|
84
|
+
`timestamp#${timestamp}|merchantId#${merchantId}|transactionId#${transactionId}`,
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// The sharding configuration for this entity.
|
|
88
|
+
sharding: {
|
|
89
|
+
// Default number of shard key characters (0 means no shard key).
|
|
90
|
+
nibbles: 0,
|
|
91
|
+
|
|
92
|
+
// Bits represented by each shard key character. 3 bits means an octal
|
|
93
|
+
// shard key, so the first character yields 8 shards and the second
|
|
94
|
+
// yields 64.
|
|
95
|
+
nibbleBits: 4,
|
|
96
|
+
|
|
97
|
+
// Scheduled increases in shard key length. Keys are expressed as
|
|
98
|
+
// millisecond UTC timestamps. In production, these should not be
|
|
99
|
+
// updated after they go into effect.
|
|
100
|
+
bumps: {
|
|
101
|
+
1676874972686: 1,
|
|
102
|
+
1708411134487: 2,
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// Extracts entity key to be hashed for shard key.
|
|
106
|
+
entityKey: ({ transactionId }) => transactionId,
|
|
107
|
+
|
|
108
|
+
// Extracts timestamp to determine shard key length.
|
|
109
|
+
timestamp: ({ timestamp }) => timestamp,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
// Will be added to every entity object. Pick a value that won't collide with
|
|
114
|
+
// entity data!
|
|
115
|
+
shardKeyToken: 'shardId',
|
|
116
|
+
};
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Usage
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
// Import an optional logger.
|
|
123
|
+
import { Logger } from '@karmaniverous/edge-logger';
|
|
124
|
+
const logger = new Logger('debug');
|
|
125
|
+
|
|
126
|
+
// Import EntityManager & config object.
|
|
127
|
+
import { EntityManager } from '@karmaniverous/entity-manager';
|
|
128
|
+
import { config } from './entityConfig.js';
|
|
129
|
+
|
|
130
|
+
// Create & configure an EntityManager (logger defaults to console object).
|
|
131
|
+
const entityManager = new EntityManager({ config, logger });
|
|
132
|
+
|
|
133
|
+
// Define a transaction object.
|
|
134
|
+
const transaction = {
|
|
135
|
+
methodId: 'methodIdValue',
|
|
136
|
+
merchantId: 'merchantIdValue',
|
|
137
|
+
timestamp: now + 1000,
|
|
138
|
+
transactionId: 'transactionIdValue',
|
|
139
|
+
userId: 'userIdValue',
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Transform the transaction for posting to the database.
|
|
143
|
+
entityManager.addKeys(transaction);
|
|
144
|
+
|
|
145
|
+
// debug: adding sharded index keys to transaction...
|
|
146
|
+
// debug: {
|
|
147
|
+
// debug: "methodId": "methodIdValue",
|
|
148
|
+
// debug: "merchantId": "merchantIdValue",
|
|
149
|
+
// debug: "timestamp": 1676869312851,
|
|
150
|
+
// debug: "transactionId": "transactionIdValue",
|
|
151
|
+
// debug: "userId": "userIdValue"
|
|
152
|
+
// debug: }
|
|
153
|
+
// debug: generated shard key '7' for transaction id 'transactionIdValue' at timestamp 1676869312851.
|
|
154
|
+
// debug:
|
|
155
|
+
// debug: done
|
|
156
|
+
// debug: {
|
|
157
|
+
// debug: "methodId": "methodIdValue",
|
|
158
|
+
// debug: "merchantId": "merchantIdValue",
|
|
159
|
+
// debug: "timestamp": 1676869312851,
|
|
160
|
+
// debug: "transactionId": "transactionIdValue",
|
|
161
|
+
// debug: "userId": "userIdValue",
|
|
162
|
+
// debug: "shardId": "7",
|
|
163
|
+
// debug: "entityPK": "transaction!7",
|
|
164
|
+
// debug: "entitySK": "timestamp#1676869312851|transactionId#transactionIdValue",
|
|
165
|
+
// debug: "merchantPK": "merchantId#merchantIdValue|transaction!7",
|
|
166
|
+
// debug: "merchantSK": "timestamp#1676869312851|methodId#methodIdValue|transactionId#transactionIdValue",
|
|
167
|
+
// debug: "methodPK": "method#methodIdValue|transaction!7",
|
|
168
|
+
// debug: "methodSK": "timestamp#1676869312851|merchantId#merchantIdValue|transactionId#transactionIdValue",
|
|
169
|
+
// debug: "userPK": "user#userIdValue|transaction!7",
|
|
170
|
+
// debug: "userSK": "timestamp#1676869312851|merchantId#merchantIdValue|transactionId#transactionIdValue"
|
|
171
|
+
// debug: }
|
|
172
|
+
|
|
173
|
+
// Extract key space for querying across shards by related entity.
|
|
174
|
+
entityManager.getKeySpace('transaction', transaction, 'userPK', 1686874972686);
|
|
175
|
+
|
|
176
|
+
// debug: getting shard key space for transaction on key 'userPK' at timestamp 1686874972686...
|
|
177
|
+
// debug: {
|
|
178
|
+
// debug: "methodId": "methodIdValue",
|
|
179
|
+
// debug: "merchantId": "merchantIdValue",
|
|
180
|
+
// debug: "timestamp": 1676876779118,
|
|
181
|
+
// debug: "transactionId": "transactionIdValue",
|
|
182
|
+
// debug: "userId": "userIdValue"
|
|
183
|
+
// debug: }
|
|
184
|
+
// debug:
|
|
185
|
+
// debug: done
|
|
186
|
+
// debug: [
|
|
187
|
+
// debug: "user#userIdValue|transaction",
|
|
188
|
+
// debug: "user#userIdValue|transaction!0",
|
|
189
|
+
// debug: "user#userIdValue|transaction!1",
|
|
190
|
+
// debug: "user#userIdValue|transaction!2",
|
|
191
|
+
// debug: "user#userIdValue|transaction!3",
|
|
192
|
+
// debug: "user#userIdValue|transaction!4",
|
|
193
|
+
// debug: "user#userIdValue|transaction!5",
|
|
194
|
+
// debug: "user#userIdValue|transaction!6",
|
|
195
|
+
// debug: "user#userIdValue|transaction!7"
|
|
196
|
+
// debug: ]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
See [unit tests](https://github.com/karmaniverous/entity-manager/blob/main/lib/EntityManager/EntityManager.test.js) for more usage examples.
|
|
200
|
+
|
|
201
|
+
## Future-Proofing
|
|
202
|
+
|
|
203
|
+
The current design provides for scaling via planned increases in shard key length. The number of shards per key character does not need to be decided until shard keys are first applied.
|
|
204
|
+
|
|
205
|
+
This design assumes that currently-defined key structures will remain stable across the life of the database, meaning new ones could be layered on but existing ones should not be changed once in use.
|
|
206
|
+
|
|
207
|
+
The same technique that provides for shard key length bumps could also be applied to such schema changes, permitting unified query across schema changes in the same manner as the package currently supports unified query across shards.
|
|
208
|
+
|
|
209
|
+
This change can be accomplished with no breaking changes to existing implementations.
|
|
210
|
+
|
|
211
|
+
# API Documentation
|
|
212
|
+
|
|
213
|
+
<a name="module_entity-manager"></a>
|
|
214
|
+
|
|
215
|
+
## entity-manager
|
|
216
|
+
|
|
217
|
+
* [entity-manager](#module_entity-manager)
|
|
218
|
+
* [.EntityManager](#module_entity-manager.EntityManager)
|
|
219
|
+
* [new exports.EntityManager(options)](#new_module_entity-manager.EntityManager_new)
|
|
220
|
+
* [.addKeys(entityToken, item, [overwrite])](#module_entity-manager.EntityManager+addKeys) ⇒ <code>object</code>
|
|
221
|
+
* [.getKeySpace(entityToken, item, keyToken, timestamp)](#module_entity-manager.EntityManager+getKeySpace) ⇒ <code>Array.<string></code>
|
|
222
|
+
|
|
223
|
+
<a name="module_entity-manager.EntityManager"></a>
|
|
224
|
+
|
|
225
|
+
### entity-manager.EntityManager
|
|
226
|
+
Manage DynamoDb entities.
|
|
227
|
+
|
|
228
|
+
**Kind**: static class of [<code>entity-manager</code>](#module_entity-manager)
|
|
229
|
+
|
|
230
|
+
* [.EntityManager](#module_entity-manager.EntityManager)
|
|
231
|
+
* [new exports.EntityManager(options)](#new_module_entity-manager.EntityManager_new)
|
|
232
|
+
* [.addKeys(entityToken, item, [overwrite])](#module_entity-manager.EntityManager+addKeys) ⇒ <code>object</code>
|
|
233
|
+
* [.getKeySpace(entityToken, item, keyToken, timestamp)](#module_entity-manager.EntityManager+getKeySpace) ⇒ <code>Array.<string></code>
|
|
234
|
+
|
|
235
|
+
<a name="new_module_entity-manager.EntityManager_new"></a>
|
|
236
|
+
|
|
237
|
+
#### new exports.EntityManager(options)
|
|
238
|
+
Create an EntityManager instance.
|
|
239
|
+
|
|
240
|
+
**Returns**: <code>EntityManager</code> - EntityManager instance.
|
|
241
|
+
**Throws**:
|
|
242
|
+
|
|
243
|
+
- <code>Error</code> If config is invalid.
|
|
244
|
+
- <code>Error</code> If logger is invalid.
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
| Param | Type | Description |
|
|
248
|
+
| --- | --- | --- |
|
|
249
|
+
| options | <code>object</code> | Options object. |
|
|
250
|
+
| [options.config] | <code>object</code> | EntityManager configuration object (see [README](https://github.com/karmaniverous/entity-manager#configuration) for a breakdown). |
|
|
251
|
+
| [options.logger] | <code>object</code> | Logger instance (defaults to console, must support error & debug methods). |
|
|
252
|
+
|
|
253
|
+
<a name="module_entity-manager.EntityManager+addKeys"></a>
|
|
254
|
+
|
|
255
|
+
#### entityManager.addKeys(entityToken, item, [overwrite]) ⇒ <code>object</code>
|
|
256
|
+
Decorate an entity item with keys.
|
|
257
|
+
|
|
258
|
+
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
259
|
+
**Returns**: <code>object</code> - Decorated entity item.
|
|
260
|
+
**Throws**:
|
|
261
|
+
|
|
262
|
+
- <code>Error</code> If entityToken is invalid.
|
|
263
|
+
- <code>Error</code> If item is invalid.
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
| Param | Type | Default | Description |
|
|
267
|
+
| --- | --- | --- | --- |
|
|
268
|
+
| entityToken | <code>string</code> | | Entity token. |
|
|
269
|
+
| item | <code>object</code> | | Entity item. |
|
|
270
|
+
| [overwrite] | <code>boolean</code> | <code>false</code> | Overwrite existing properties. |
|
|
271
|
+
|
|
272
|
+
<a name="module_entity-manager.EntityManager+getKeySpace"></a>
|
|
273
|
+
|
|
274
|
+
#### entityManager.getKeySpace(entityToken, item, keyToken, timestamp) ⇒ <code>Array.<string></code>
|
|
275
|
+
Return an array of sharded keys valid for a given entity token & timestamp.
|
|
276
|
+
|
|
277
|
+
**Kind**: instance method of [<code>EntityManager</code>](#module_entity-manager.EntityManager)
|
|
278
|
+
**Returns**: <code>Array.<string></code> - Array of keys.
|
|
279
|
+
**Throws**:
|
|
280
|
+
|
|
281
|
+
- <code>Error</code> If entityToken is invalid.
|
|
282
|
+
- <code>Error</code> If item is invalid.
|
|
283
|
+
- <code>Error</code> If keyToken is invalid.
|
|
284
|
+
- <code>Error</code> If timestamp is invalid.
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
| Param | Type | Description |
|
|
288
|
+
| --- | --- | --- |
|
|
289
|
+
| entityToken | <code>string</code> | Entity token. |
|
|
290
|
+
| item | <code>object</code> | Entity item. |
|
|
291
|
+
| keyToken | <code>string</code> | Key token. |
|
|
292
|
+
| timestamp | <code>number</code> | Timestamp. |
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
See more great templates and other tools on
|
|
298
|
+
[my GitHub Profile](https://github.com/karmaniverous)!
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.EntityManager = void 0;
|
|
7
|
+
var _sortedUniq2 = _interopRequireDefault(require("lodash/sortedUniq"));
|
|
8
|
+
var _forEach2 = _interopRequireDefault(require("lodash/forEach"));
|
|
9
|
+
var _isNil2 = _interopRequireDefault(require("lodash/isNil"));
|
|
10
|
+
var _PrivateEntityManager = require("./PrivateEntityManager.js");
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
+
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
|
|
13
|
+
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
|
|
14
|
+
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
|
|
15
|
+
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
|
|
16
|
+
function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; }
|
|
17
|
+
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
|
|
18
|
+
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; } }
|
|
19
|
+
var _entityManager = /*#__PURE__*/new WeakMap();
|
|
20
|
+
/**
|
|
21
|
+
* Manage DynamoDb entities.
|
|
22
|
+
*
|
|
23
|
+
* @class
|
|
24
|
+
*/
|
|
25
|
+
class EntityManager {
|
|
26
|
+
/**
|
|
27
|
+
* Create an EntityManager instance.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} options - Options object.
|
|
30
|
+
* @param {object} [options.config] - EntityManager configuration object (see {@link https://github.com/karmaniverous/entity-manager#configuration README} for a breakdown).
|
|
31
|
+
* @param {object} [options.logger] - Logger instance (defaults to console, must support error & debug methods).
|
|
32
|
+
* @returns {EntityManager} EntityManager instance.
|
|
33
|
+
* @throws {Error} If config is invalid.
|
|
34
|
+
* @throws {Error} If logger is invalid.
|
|
35
|
+
*/
|
|
36
|
+
constructor(_ref) {
|
|
37
|
+
let {
|
|
38
|
+
config,
|
|
39
|
+
logger
|
|
40
|
+
} = _ref;
|
|
41
|
+
_classPrivateFieldInitSpec(this, _entityManager, {
|
|
42
|
+
writable: true,
|
|
43
|
+
value: void 0
|
|
44
|
+
});
|
|
45
|
+
_classPrivateFieldSet(this, _entityManager, new _PrivateEntityManager.PrivateEntityManager({
|
|
46
|
+
config,
|
|
47
|
+
logger
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Decorate an entity item with keys.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} entityToken - Entity token.
|
|
55
|
+
* @param {object} item - Entity item.
|
|
56
|
+
* @param {boolean} [overwrite=false] - Overwrite existing properties.
|
|
57
|
+
* @returns {object} Decorated entity item.
|
|
58
|
+
* @throws {Error} If entityToken is invalid.
|
|
59
|
+
* @throws {Error} If item is invalid.
|
|
60
|
+
*/
|
|
61
|
+
addKeys(entityToken, item) {
|
|
62
|
+
let overwrite = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
|
|
63
|
+
_classPrivateFieldGet(this, _entityManager).logger.debug(`adding sharded index keys to ${entityToken}${overwrite ? ' with overwrite' : ''}...`, item);
|
|
64
|
+
|
|
65
|
+
// Get entity config.
|
|
66
|
+
const {
|
|
67
|
+
shardKeyToken
|
|
68
|
+
} = _classPrivateFieldGet(this, _entityManager);
|
|
69
|
+
const {
|
|
70
|
+
keys,
|
|
71
|
+
sharding
|
|
72
|
+
} = _classPrivateFieldGet(this, _entityManager).getEntityConfig(entityToken);
|
|
73
|
+
|
|
74
|
+
// Add shardKey.
|
|
75
|
+
_classPrivateFieldGet(this, _entityManager).validateItem(item);
|
|
76
|
+
if (overwrite || (0, _isNil2.default)(item[shardKeyToken])) {
|
|
77
|
+
const entityKey = sharding.entityKey(item);
|
|
78
|
+
const timestamp = sharding.timestamp(item);
|
|
79
|
+
item[shardKeyToken] = _classPrivateFieldGet(this, _entityManager).getShardKey(entityToken, entityKey, timestamp);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Add keys.
|
|
83
|
+
(0, _forEach2.default)(keys, (getValue, key) => {
|
|
84
|
+
if (overwrite || (0, _isNil2.default)(item[key])) item[key] = getValue(item);
|
|
85
|
+
});
|
|
86
|
+
_classPrivateFieldGet(this, _entityManager).logger.debug('done', item);
|
|
87
|
+
return item;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Return an array of sharded keys valid for a given entity token & timestamp.
|
|
92
|
+
*
|
|
93
|
+
* @param {string} entityToken - Entity token.
|
|
94
|
+
* @param {object} item - Entity item.
|
|
95
|
+
* @param {string} keyToken - Key token.
|
|
96
|
+
* @param {number} timestamp - Timestamp.
|
|
97
|
+
* @returns {string[]} Array of keys.
|
|
98
|
+
* @throws {Error} If entityToken is invalid.
|
|
99
|
+
* @throws {Error} If item is invalid.
|
|
100
|
+
* @throws {Error} If keyToken is invalid.
|
|
101
|
+
* @throws {Error} If timestamp is invalid.
|
|
102
|
+
*/
|
|
103
|
+
getKeySpace(entityToken, item, keyToken) {
|
|
104
|
+
let timestamp = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : Date.now();
|
|
105
|
+
_classPrivateFieldGet(this, _entityManager).logger.debug(`getting shard key space for ${entityToken} on key '${keyToken}' at timestamp ${timestamp}...`, item);
|
|
106
|
+
const shardKeySpace = _classPrivateFieldGet(this, _entityManager).getShardKeySpace(entityToken, timestamp);
|
|
107
|
+
const result = (0, _sortedUniq2.default)(shardKeySpace.map(shardKey => _classPrivateFieldGet(this, _entityManager).getKeyGenerator(entityToken, keyToken)({
|
|
108
|
+
...item,
|
|
109
|
+
[_classPrivateFieldGet(this, _entityManager).shardKeyToken]: shardKey
|
|
110
|
+
})));
|
|
111
|
+
_classPrivateFieldGet(this, _entityManager).logger.debug('done', result);
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.EntityManager = EntityManager;
|