electrodb 1.12.1 → 2.0.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/src/service.js CHANGED
@@ -243,13 +243,8 @@ class Service {
243
243
 
244
244
  cleanseRetrievedData(collection = "", entities, data = {}, config = {}) {
245
245
  if (config.raw) {
246
- if (config._isPagination) {
247
- return [data.LastEvaluatedKey, data];
248
- } else {
249
- return data;
250
- }
246
+ return data;
251
247
  }
252
-
253
248
  data.Items = data.Items || [];
254
249
  let index = this.collectionSchema[collection].index;
255
250
  let results = {};
@@ -268,18 +263,15 @@ class Service {
268
263
  if (!entityAlias) {
269
264
  continue;
270
265
  }
271
-
272
266
  // pager=false because we don't want the entity trying to parse the lastEvaluatedKey
273
- let items = this.collectionSchema[collection].entities[entityAlias].formatResponse({Item: record}, index, {...config, pager: false});
274
- results[entityAlias].push(items);
275
- }
276
-
277
- if (config._isPagination) {
278
- let page = this._formatReturnPager(config, index, data.LastEvaluatedKey, data.Items[data.Items.length - 1]);
279
- return [page, results];
280
- } else {
281
- return results;
267
+ let items = this.collectionSchema[collection].entities[entityAlias].formatResponse({Item: record}, index, {
268
+ ...config,
269
+ pager: false,
270
+ parse: undefined
271
+ });
272
+ results[entityAlias].push(items.data);
282
273
  }
274
+ return results;
283
275
  }
284
276
 
285
277
  findKeyOwner(lastEvaluatedKey) {
@@ -287,92 +279,25 @@ class Service {
287
279
  .find((entity) => entity.ownsLastEvaluatedKey(lastEvaluatedKey));
288
280
  }
289
281
 
290
- findItemPagerOwner(collection, pager = {}) {
291
- if (this.collectionSchema[collection] === undefined) {
292
- throw new Error("Invalid collection");
293
- }
294
- let matchingEntities = [];
295
- if (pager === null) {
296
- return matchingEntities;
297
- }
298
- for (let entity of Object.values(this.collectionSchema[collection].entities)) {
299
- if (entity.ownsPager(pager, this.collectionSchema[collection].index)) {
300
- matchingEntities.push(entity);
301
- }
302
- }
303
- return matchingEntities;
304
- }
305
-
306
- findNamedPagerOwner(pager = {}) {
307
- let identifiers = this._getEntityIdentifiers(this.entities);
308
- for (let identifier of identifiers) {
309
- let hasCorrectFieldProperties = typeof pager[identifier.nameField] === "string" && typeof pager[identifier.versionField] === "string";
310
- let hasMatchingFieldValues = pager[identifier.nameField] === identifier.name && pager[identifier.versionField] === identifier.version;
311
- if (hasCorrectFieldProperties && hasMatchingFieldValues) {
312
- return identifier.entity;
313
- }
314
- }
315
- }
316
-
317
- expectPagerOwner(type, collection, pager) {
318
- if (type === Pager.raw) {
319
- return Object.values(this.collectionSchema[collection].entities)[0];
320
- } else if (type === Pager.named || type === undefined) {
321
- let owner = this.findNamedPagerOwner(pager);
322
- if (owner === undefined) {
323
- throw new e.ElectroError(e.ErrorCodes.NoOwnerForPager, "Supplied Pager does not resolve to Entity within Service");
324
- }
325
- return owner;
326
- } else if (type === Pager.item) {
327
- let owners = this.findItemPagerOwner(collection, pager);
328
- if (owners.length === 1) {
329
- return owners[0];
330
- } else {
331
- throw new e.ElectroError(e.ErrorCodes.PagerNotUnique, "Supplied Pager did not resolve to single Entity");
332
- }
333
- } else {
334
- throw new e.ElectroError(e.ErrorCodes.InvalidOptions, `Invalid value for option "pager" provider: "${pager}". Allowed values include ${u.commaSeparatedString(Object.keys(Pager))}.`)
282
+ expectKeyOwner(lastEvaluatedKey) {
283
+ const owner = this.findKeyOwner(lastEvaluatedKey);
284
+ if (owner === undefined) {
285
+ throw new e.ElectroError(e.ErrorCodes.NoOwnerForCursor, `Supplied cursor does not resolve to Entity within the Service ${this.service.name}`);
335
286
  }
287
+ return owner;
336
288
  }
337
289
 
338
- findPagerIdentifiers(collection, pager = {}) {
339
- if (this.collectionSchema[collection] === undefined) {
340
- throw new Error("Invalid collection");
341
- }
342
- let matchingIdentifiers = [];
343
- if (pager === null) {
344
- return matchingIdentifiers;
345
- }
346
- for (let entity of Object.values(this.collectionSchema[collection].entities)) {
347
- if (entity.ownsPager(pager, this.collectionSchema[collection].index)) {
348
- matchingIdentifiers.push({
349
- [entity.identifiers.entity]: entity.getName(),
350
- [entity.identifiers.version]: entity.getVersion(),
351
- });
352
- }
353
- }
354
- return matchingIdentifiers;
290
+ findCursorOwner(cursor) {
291
+ return Object.values(this.entities)
292
+ .find(entity => entity.ownsCursor(cursor));
355
293
  }
356
294
 
357
- _formatReturnPager(config, index, lastEvaluatedKey, lastReturned) {
358
- if (config.pager === "raw") {
359
- return lastEvaluatedKey;
360
- } else if (!lastEvaluatedKey) {
361
- return null;
362
- } else {
363
- let entity = this.findKeyOwner(lastEvaluatedKey);
364
- if (!entity) {
365
- return lastReturned !== undefined
366
- ? this._formatReturnPager(config, index, lastReturned)
367
- : null
368
- }
369
- let page = entity._formatKeysToItem(index, lastEvaluatedKey);
370
- if (config.pager === "named") {
371
- page[entity.identifiers.entity] = entity.getName();
372
- page[entity.identifiers.version] = entity.getVersion();
373
- }
374
- return page;
295
+ expectCursorOwner(cursor) {
296
+ const owner = this.findCursorOwner(cursor);
297
+ if (owner === undefined) {
298
+ throw new e.ElectroError(e.ErrorCodes.NoOwnerForCursor, `Supplied cursor does not resolve to Entity within the Service ${this.service.name}`);
375
299
  }
300
+ return owner;
376
301
  }
377
302
 
378
303
  _getTableName() {
@@ -389,21 +314,7 @@ class Service {
389
314
  _makeCollectionChain(name = "", attributes = {}, initialClauses = {}, expressions = {}, entities = {}, entity = {}, facets = {}) {
390
315
  let filterBuilder = new FilterFactory(attributes, FilterOperations);
391
316
  let whereBuilder = new WhereFactory(attributes, FilterOperations);
392
-
393
- let pageClause = {...initialClauses.page};
394
- let pageAction = initialClauses.page.action;
395
- pageClause.action = (entity, state, page = null, options = {}) => {
396
- try {
397
- if (page === null) {
398
- return pageAction(entity, state, page, options);
399
- }
400
- let owner = this.expectPagerOwner(options.pager, name, page);
401
- return pageAction(owner, state, page, options);
402
- } catch(err) {
403
- return Promise.reject(err);
404
- }
405
- }
406
- let clauses = {...initialClauses, page: pageClause};
317
+ let clauses = {...initialClauses};
407
318
 
408
319
  clauses = filterBuilder.injectFilterClauses(clauses);
409
320
  clauses = whereBuilder.injectWhereClauses(clauses);
@@ -412,6 +323,14 @@ class Service {
412
323
  // expressions, // DynamoDB doesnt return what I expect it would when provided with these entity filters
413
324
  parse: (options, data) => {
414
325
  return this.cleanseRetrievedData(name, entities, data, options);
326
+ },
327
+ formatCursor: {
328
+ serialize: (key) => {
329
+ return this.expectKeyOwner(key).serializeCursor(key);
330
+ },
331
+ deserialize: (cursor) => {
332
+ return this.expectCursorOwner(cursor).deserilizeCursor(cursor);
333
+ }
415
334
  }
416
335
  };
417
336
 
package/src/types.js CHANGED
@@ -110,7 +110,8 @@ const AttributeMutationMethods = {
110
110
  const Pager = {
111
111
  raw: "raw",
112
112
  named: "named",
113
- item: "item"
113
+ item: "item",
114
+ cursor: "cursor"
114
115
  }
115
116
 
116
117
  const UnprocessedTypes = {
@@ -194,6 +195,15 @@ const TerminalOperation = {
194
195
  page: 'page',
195
196
  }
196
197
 
198
+ const AllPages = 'all';
199
+
200
+ const ResultOrderOption = {
201
+ 'asc': true,
202
+ 'desc': false
203
+ };
204
+
205
+ const ResultOrderParam = 'ScanIndexForward';
206
+
197
207
  module.exports = {
198
208
  Pager,
199
209
  KeyTypes,
@@ -223,5 +233,8 @@ module.exports = {
223
233
  AttributeProxySymbol,
224
234
  ElectroInstanceTypes,
225
235
  EventSubscriptionTypes,
226
- AttributeMutationMethods
236
+ AttributeMutationMethods,
237
+ AllPages,
238
+ ResultOrderOption,
239
+ ResultOrderParam,
227
240
  };
package/src/util.js CHANGED
@@ -161,6 +161,29 @@ function getUnique(arr1, arr2) {
161
161
  ]));
162
162
  }
163
163
 
164
+ const cursorFormatter = {
165
+ serialize: (key) => {
166
+ if (!key) {
167
+ return null;
168
+ } else if (typeof val !== 'string') {
169
+ key = JSON.stringify(key);
170
+ }
171
+ return Buffer.from(key).toString('base64url');
172
+ },
173
+ deserialize: (cursor) => {
174
+ if (!cursor) {
175
+ return undefined;
176
+ } else if (typeof cursor !== 'string') {
177
+ throw new Error(`Invalid cursor provided, expected type 'string' recieved: ${JSON.stringify(cursor)}`);
178
+ }
179
+ try {
180
+ return JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
181
+ } catch(err) {
182
+ throw new Error('Unable to parse cursor');
183
+ }
184
+ }
185
+ }
186
+
164
187
  module.exports = {
165
188
  getUnique,
166
189
  batchItems,
@@ -171,6 +194,7 @@ module.exports = {
171
194
  genericizeJSONPath,
172
195
  commaSeparatedString,
173
196
  formatAttributeCasing,
197
+ cursorFormatter,
174
198
  applyBetaModelOverrides,
175
199
  formatIndexNameForDisplay,
176
200
  BatchGetOrderMaintainer,
package/src/where.js CHANGED
@@ -120,7 +120,7 @@ class WhereFactory {
120
120
  injected[name] = {
121
121
  name,
122
122
  action: this.buildClause(filter),
123
- children: ["params", "go", "page", "where", ...modelFilters],
123
+ children: ["params", "go", "where", ...modelFilters],
124
124
  };
125
125
  }
126
126
  filterChildren.push("where");
@@ -129,7 +129,7 @@ class WhereFactory {
129
129
  action: (entity, state, fn) => {
130
130
  return this.buildClause(fn)(entity, state);
131
131
  },
132
- children: ["params", "go", "page", "where", ...modelFilters],
132
+ children: ["params", "go", "where", ...modelFilters],
133
133
  };
134
134
  for (let parent of filterParents) {
135
135
  injected[parent] = { ...injected[parent] };
package/notes DELETED
@@ -1,45 +0,0 @@
1
- /*
2
-
3
- todo: better param typing
4
- done: get typing
5
- todo: get implementation
6
- done: get[] typing
7
- todo: get[] implementation
8
- done: delete typing
9
- todo: delete implementation
10
- done: delete[] typing
11
- todo: delete[] implementation
12
- done: put[] typing
13
- todo: put[] implementation
14
- done: put typing
15
- todo: put implementation
16
- todo: update typing
17
- done: update implementation
18
- done: SetRecord
19
- done: RemoveRecord
20
- done: DataUpdateMethodRecord
21
-
22
- done: scan typing
23
- todo: scan implementation
24
- done: query typing
25
- todo: query implementation
26
- done: find typing
27
- todo: find implementation
28
- done: match typing
29
- todo: match implementation
30
- done: parse typing
31
- todo: parse implementation (requires cursor?)
32
- todo: collection query
33
- todo: collection impl
34
-
35
- todo: add where to go?
36
- todo: rethink option names
37
-
38
- todo: add cursor to query options
39
- todo: remove old doc notes on `join` and slim down `service`
40
-
41
-
42
-
43
- find and test any add on set and number
44
- try number enum?
45
- integration test enum set ts
package/output DELETED
@@ -1,106 +0,0 @@
1
- page {
2
- "data": [
3
- {
4
- "prop2": "e3b142b1-e5d7-4b17-8a66-38fcdf6ef1ef",
5
- "prop1": "value1",
6
- "prop4": "value4"
7
- },
8
- {
9
- "prop2": "e8ed19db-3ac4-45d6-8fb8-281ada117e96",
10
- "prop1": "value1",
11
- "prop4": "value4"
12
- },
13
- {
14
- "prop2": "dcca03f5-7478-4afb-9297-20a68ff56559",
15
- "prop1": "value1",
16
- "prop4": "value4"
17
- },
18
- {
19
- "prop2": "03ccd79e-c5e5-4179-8213-196eafcbd4b9",
20
- "prop1": "value1",
21
- "prop4": "value4"
22
- },
23
- {
24
- "prop2": "2900735e-aaa4-4661-a52d-7aecb752e694",
25
- "prop1": "value1",
26
- "prop4": "value4"
27
- },
28
- {
29
- "prop2": "0f1da461-5257-4f6c-8cb3-af7b56819100",
30
- "prop1": "value1",
31
- "prop4": "value4"
32
- },
33
- {
34
- "prop2": "7562c43b-8ac9-483c-b950-4f0e1366c11a",
35
- "prop1": "value1",
36
- "prop4": "value4"
37
- },
38
- {
39
- "prop2": "ee844e97-dc1c-4242-bd2b-45685e39d7fd",
40
- "prop1": "value1",
41
- "prop4": "value4"
42
- },
43
- {
44
- "prop2": "4b55e242-9875-412e-b483-24e51dabf6c8",
45
- "prop1": "value1",
46
- "prop4": "value4"
47
- },
48
- {
49
- "prop2": "77a5b66e-c658-4ec6-9630-4d2469653bd4",
50
- "prop1": "value1",
51
- "prop4": "value4"
52
- },
53
- {
54
- "prop2": "194efbfe-7be6-4662-9982-a1c8eb6ab36d",
55
- "prop1": "value1",
56
- "prop4": "value4"
57
- },
58
- {
59
- "prop2": "value2",
60
- "prop1": "value1",
61
- "prop4": "value4"
62
- },
63
- {
64
- "prop2": "516d8256-3b3e-41e1-a44a-0720fe748e23",
65
- "prop1": "value1",
66
- "prop4": "value4"
67
- },
68
- {
69
- "prop2": "35bf3a7e-9dff-4817-a157-2be09c5fc8a8",
70
- "prop1": "value1",
71
- "prop4": "value4"
72
- },
73
- {
74
- "prop2": "3836452b-6c00-415e-85ce-a9596644b6eb",
75
- "prop1": "value1",
76
- "prop4": "value4"
77
- },
78
- {
79
- "prop2": "value2abc",
80
- "prop1": "value1",
81
- "prop4": "value4"
82
- },
83
- {
84
- "prop2": "887e5301-113c-4ca6-b6d0-ebaf27e217d5",
85
- "prop1": "value1",
86
- "prop4": "value4"
87
- },
88
- {
89
- "prop2": "6c6d9363-3821-40c8-b3d0-7ec2c6d3f1eb",
90
- "prop1": "value1",
91
- "prop4": "value4"
92
- },
93
- {
94
- "prop2": "fcff14a3-ffa9-498e-a0ae-596c747b027b",
95
- "prop1": "value1",
96
- "prop4": "value4"
97
- },
98
- {
99
- "prop2": "1d199eeb-ccc1-4c25-a1b6-1685126d44b3",
100
- "prop1": "value1",
101
- "prop4": "value4"
102
- }
103
- ],
104
- "count": 2,
105
- "next": null
106
- }