electrodb 0.10.4 → 0.11.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Tyler W. Walch
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -73,11 +73,17 @@ StoreLocations.query
73
73
  ## Table of Contents
74
74
 
75
75
  - [ElectroDB](#electrodb)
76
+ + [Try it out for yourself! https://runkit.com/tywalch/electrodb-building-queries](#try-it-out-for-yourself--https---runkitcom-tywalch-electrodb-building-queries)
76
77
  * [Features](#features)
78
+ + [Try it out for yourself! https://runkit.com/tywalch/electrodb-building-queries](#try-it-out-for-yourself--https---runkitcom-tywalch-electrodb-building-queries-1)
77
79
  * [Table of Contents](#table-of-contents)
78
80
  - [Installation](#installation)
79
81
  - [Usage](#usage)
80
82
  * [TypeScript Support](#typescript-support)
83
+ + [Exported Types](#exported-types)
84
+ - [EntityItem](#entityitem)
85
+ - [CreateEntityItem](#createentityitem)
86
+ - [CreateEntityItem](#createentityitem-1)
81
87
  - [Entities and Services](#entities-and-services)
82
88
  - [Entities](#entities)
83
89
  - [Services](#services)
@@ -90,8 +96,11 @@ StoreLocations.query
90
96
  + [Simple Syntax](#simple-syntax)
91
97
  + [Expanded Syntax](#expanded-syntax)
92
98
  - [Enum Attributes](#enum-attributes)
93
- - [Any Attributes](#any-attributes)
99
+ - [Attribute Definition](#attribute-definition)
94
100
  * [Attribute Getters and Setters](#attribute-getters-and-setters)
101
+ * [Attribute Watching](#attribute-watching)
102
+ * [Calculated Attributes](#calculated-attributes)
103
+ * [Virtual Attributes](#virtual-attributes)
95
104
  - [Attribute Validation](#attribute-validation)
96
105
  * [Indexes](#indexes)
97
106
  + [Indexes Without Sort Keys](#indexes-without-sort-keys)
@@ -226,6 +235,67 @@ As of writing this, this functionality is still a work in progress, and enforcem
226
235
 
227
236
  If you experience any issues using TypeScript with ElectroDB, your feedback is very important, please create a github issue, and it can be addressed.
228
237
 
238
+ ### Exported Types
239
+
240
+ The following types are exported for easier use while using ElectroDB with TypeScript:
241
+
242
+ #### EntityItem
243
+
244
+ This type represents an item as it's returned from a query to DynamoDB.
245
+
246
+ _Definition:_
247
+
248
+ ```typescript
249
+ export type EntityItem<E extends Entity<any, any, any, any>> =
250
+ E extends Entity<infer A, infer F, infer C, infer S>
251
+ ? ResponseItem<A, F, C, S>
252
+ : never;
253
+ ```
254
+
255
+ _Use:_
256
+
257
+ ```typescript
258
+ type Thing = EntityItem<typeof YourEntityInstance>;
259
+ ```
260
+
261
+ #### CreateEntityItem
262
+
263
+ This type represents an item that you would pass your entity's `put` or `create` method
264
+
265
+ _Definition:_
266
+
267
+ ```typescript
268
+ export type CreateEntityItem<E extends Entity<any, any, any, any>> =
269
+ E extends Entity<infer A, infer F, infer C, infer S>
270
+ ? PutItem<A, F, C, S>
271
+ : never;
272
+ ```
273
+
274
+ _Use:_
275
+
276
+ ```typescript
277
+ type NewThing = CreateEntityItem<typeof YourEntityInstance>;
278
+ ```
279
+
280
+ #### CreateEntityItem
281
+
282
+ This type represents an item that you would pass your entity's `create` or `update` method to `set`
283
+
284
+ _Definition:_
285
+
286
+ ```typescript
287
+ export type UpdateEntityItem<E extends Entity<any, any, any, any>> =
288
+ E extends Entity<infer A, infer F, infer C, infer S>
289
+ ? SetItem<A, F, C, S>
290
+ : never;
291
+ ```
292
+
293
+ _Use:_
294
+
295
+ ```typescript
296
+ type UpdateProperties = UpdateEntityItem<typeof YourEntityInstance>;
297
+ ```
298
+
229
299
  # Entities and Services
230
300
  > To see full examples of ***ElectroDB*** in action, go to the [Examples](#examples) section.
231
301
 
@@ -2227,28 +2297,30 @@ let stores = MallStores.query
2227
2297
 
2228
2298
  ### Page
2229
2299
 
2230
- > As of September 29th 2020 the `.page()` now returns the facets that make up the `ExclusiveStartKey` instead of the `ExclusiveStartKey` itself. To get back only the `ExclusiveStartKey`, add the flag `exclusiveStartKeyRaw` to your query options. If you treated this value opaquely no changes are needed, or if you used the `raw` flag.
2300
+ > As of September 29th 2020 the `.page()` now returns the facets that make up the `ExclusiveStartKey` instead of the `ExclusiveStartKey` itself. To get back only the `ExclusiveStartKey`, add the [query option](#query-options) `{pager: "raw"}` to your query options. If you treated this value opaquely no changes are needed, or if you used the `raw` flag.
2231
2301
 
2232
2302
  The `page` method _ends_ a query chain, and asynchronously queries DynamoDB with the `client` provided in the model. Unlike the `.go()`, the `.page()` method returns a tuple.
2233
2303
 
2234
- The first element for [Entity](#entities) page query is the "page": an object contains the facets that make up the `ExclusiveStartKey` that is returned by the DynamoDB client. This is very useful in multi-tenant applications where only some facets are exposed to the client, or there is a need to prevent leaking keys between entities. If there is no `ExclusiveStartKey` this value will be null. On subsequent calls to `.page()`, pass the results returned from the previous call to `.page()` or construct the facets yourself.
2304
+ The first element for a page query is the "pager": an object contains the facets that make up the `ExclusiveStartKey` that is returned by the DynamoDB client. This is very useful in multi-tenant applications where only some facets are exposed to the client, or there is a need to prevent leaking keys between entities. If there is no `ExclusiveStartKey` this value will be null. On subsequent calls to `.page()`, pass the results returned from the previous call to `.page()` or construct the facets yourself.
2235
2305
 
2236
- The first element for [Collection](#collections) page query is the `ExclusiveStartKey` as it was returned by the DynamoDB client.
2306
+ The "pager" includes the associated entity's Identifiers.
2237
2307
 
2238
- > Note: It is *highly recommended* to use the `lastEvaluatedKeyRaw` flag when using `.page()` in conjunction with scans. This is because when using scan on large tables the docClient may return an `ExclusiveStartKey` for a record that does not belong to entity making the query (regardless of the filters set). In these cases ElectroDB will return null (to avoid leaking the keys of other entities) when further pagination may be needed to find your records.
2308
+ > Note: It is *highly recommended* to use the [query option](#query-options) `pager: "raw""` flag when using `.page()` with `scan` operations. This is because when using scan on large tables the docClient may return an `ExclusiveStartKey` for a record that does not belong to entity making the query (regardless of the filters set). In these cases ElectroDB will return null (to avoid leaking the keys of other entities) when further pagination may be needed to find your records.
2239
2309
 
2240
2310
  The second element is the results of the query, exactly as it would be returned through a `query` operation.
2241
2311
 
2242
2312
  > Note: When calling `.page()` the first argument is reserved for the "page" returned from a previous query, the second parameter is for Query Options. For more information on the options available in the `config` object, check out the section [Query Options](#query-options).
2243
2313
 
2314
+ #### Entity Pagination
2315
+
2244
2316
  ```javascript
2245
- let [page, stores] = await MallStores.query
2317
+ let [next, stores] = await MallStores.query
2246
2318
  .leases({ mallId })
2247
- .page();
2319
+ .page(); // no "pager" passed to `.page()`
2248
2320
 
2249
2321
  let [pageTwo, moreStores] = await MallStores.query
2250
2322
  .leases({ mallId })
2251
- .page(page, {});
2323
+ .page(next, {}); // the "pager" from the first query (`next`) passed to the second query
2252
2324
 
2253
2325
  // page:
2254
2326
  // {
@@ -2256,6 +2328,8 @@ let [pageTwo, moreStores] = await MallStores.query
2256
2328
  // mallId: "EastPointe",
2257
2329
  // buildingId: "BuildingA1",
2258
2330
  // unitId: "B47"
2331
+ // __edb_e__: "MallStore",
2332
+ // __edb_v__: "version"
2259
2333
  // }
2260
2334
 
2261
2335
  // stores
@@ -2283,6 +2357,88 @@ let [pageTwo, moreStores] = await MallStores.query
2283
2357
  // }]
2284
2358
  ```
2285
2359
 
2360
+ #### Service Pagination
2361
+ Pagination with services is also possible. Similar to [Entity Pagination](#entity-pagination), calling the `.page()` method returns a `[pager, results]` tuple. Also similar to pagination on Entities, the pager object returned by default is a deconstruction of the returned LastEvaluatedKey.
2362
+
2363
+ #### Pager Query Options
2364
+
2365
+ The `.page()` method also accepts [Query Options](#query-options) just like the `.go()` and `.params()` methods. Unlike those methods, however, the `.page()` method accepts Query Options as the _second_ parameter (the first parameter is reserved for the "pager").
2366
+
2367
+ A notable Query Option, that is available only to the `.page()` method, is an option called `pager`. This property defines the post-processing ElectroDB should perform on a returned `LastEvaluatedKey`, as well as how ElectroDB should interpret an _incoming_ pager, to use as an ExclusiveStartKey.
2368
+
2369
+ The three options for the query option `pager` are as follows:
2370
+
2371
+ ```javascript
2372
+ // LastEvaluatedKey
2373
+ {
2374
+ pk: '$taskapp#country_united states of america#state_oregon',
2375
+ sk: '$offices_1#city_power#zip_34706#office_mobile branch',
2376
+ gsi1pk: '$taskapp#office_mobile branch',
2377
+ gsi1sk: '$workplaces#offices_1'
2378
+ }
2379
+ ```
2380
+
2381
+ **"named" (default):** By default, ElectroDB will deconstruct the LastEvaluatedKey returned by the DocClient into it's individual facet parts. The "named" option, chosen by default, also includes the Entity's column "identifiers" -- this is useful with Services where destructured pagers may be identical between more than one Entity in that Service.
2382
+
2383
+ ```javascript
2384
+ // {pager: "named"} | {pager: undefined}
2385
+ {
2386
+ "city": "power",
2387
+ "country": "united states of america",
2388
+ "state": "oregon",
2389
+ "zip": "34706",
2390
+ "office": "mobile branch",
2391
+ "__edb_e__": "offices",
2392
+ "__edb_v__": "1"
2393
+ }
2394
+ ```
2395
+
2396
+ **"item":** Similar to "named", however without the Entity's "identifiers". If two Entities with a service have otherwise identical index definitions, using the "item" pager option can result in errors while paginating a Collection. If this is not a concern with your Service, or you are paginating with only an Entity, this option could be preferable because it has fewer properties.
2397
+
2398
+ ```javascript
2399
+ // {pager: "item"}
2400
+ {
2401
+ "city": "power",
2402
+ "country": "united states of america",
2403
+ "state": "oregon",
2404
+ "zip": "34706",
2405
+ "office": "mobile branch",
2406
+ }
2407
+ ```
2408
+
2409
+ **"raw":** The `"raw"` option returns the LastEvaluatedKey as it was returned by the DynamoDB DocClient.
2410
+
2411
+ ```javascript
2412
+ // {pager: "raw"}
2413
+ {
2414
+ pk: '$taskapp#country_united states of america#state_oregon',
2415
+ sk: '$offices_1#city_power#zip_34706#office_mobile branch',
2416
+ gsi1pk: '$taskapp#office_mobile branch',
2417
+ gsi1sk: '$workplaces#offices_1'
2418
+ }
2419
+ ```
2420
+
2421
+ ##### Pagination Example
2422
+
2423
+ Simple pagination example:
2424
+
2425
+ ```javascript
2426
+ async function getAllStores(mallId) {
2427
+ let stores = [];
2428
+ let pager = null;
2429
+
2430
+ do {
2431
+ let [next, results] = await MallStores.query
2432
+ .leases({ mallId })
2433
+ .page(pager);
2434
+ stores = [...stores, ...results];
2435
+ pager = next;
2436
+ } while(pager !== null);
2437
+
2438
+ return stores;
2439
+ }
2440
+ ```
2441
+
2286
2442
  ## Query Examples
2287
2443
  For a comprehensive and interactive guide to build queries please visit this runkit: https://runkit.com/tywalch/electrodb-building-queries.
2288
2444
 
@@ -2361,21 +2517,21 @@ By default, **ElectroDB** enables you to work with records as the names and prop
2361
2517
  table?: string
2362
2518
  raw?: boolean
2363
2519
  includeKeys?: boolean
2364
- lastEvaluatedKeyRaw?: boolean
2520
+ pager?: "raw" | "named" | "item"
2365
2521
  originalErr?: boolean
2366
2522
  concurrent?: number
2367
2523
  };
2368
2524
  ```
2369
2525
 
2370
- | Option | Description |
2371
- | ----------- | ----------- |
2372
- | params | Properties added to this object will be merged onto the params sent to the document client. Any conflicts with **ElectroDB** will favor the params specified here. |
2373
- | table | Use a different table than the one defined in the [Service Options](#service-options) |
2374
- | raw | Returns query results as they were returned by the docClient.
2375
- | includeKeys | By default, **ElectroDB** does not return partition, sort, or global keys in its response. |
2376
- | lastEvaluatedKeyRaw | Used in batch processing and `.pages()` calls to override ElectroDBs default behaviour to break apart `LastEvaluatedKeys` or the `Unprocessed` records into facets. See more detail about this in the sections for [Pages](#page), [BatchGet](#batch-get), [BatchDelete](#batch-write-delete-records), and [BatchPut](#batch-write-put-records). |
2377
- | originalErr | By default, **ElectroDB** alters the stacktrace of any exceptions thrown by the DynamoDB client to give better visibility to the developer. Set this value equal to `true` to turn off this functionality and return the error unchanged. |
2378
- | concurrent | (default: 1) When performing batch operations, how many requests (1 batch operation == 1 request) to DynamoDB should ElectroDB make at one time. Be mindful of your DynamoDB throughput configurations |
2526
+ | Option | Default | Description |
2527
+ | ----------- | :------------------: | ----------- |
2528
+ | params | `{}` | Properties added to this object will be merged onto the params sent to the document client. Any conflicts with **ElectroDB** will favor the params specified here. |
2529
+ | table | _(from constructor)_ | Use a different table than the one defined in the [Service Options](#service-options) |
2530
+ | raw | `false` | Returns query results as they were returned by the docClient.
2531
+ | includeKeys | `false` | By default, **ElectroDB** does not return partition, sort, or global keys in its response. |
2532
+ | pager | `"named"` | Used in batch processing and `.pages()` calls to override ElectroDBs default behaviour to break apart `LastEvaluatedKeys` or the `Unprocessed` records into facets. See more detail about this in the sections for [Pager Query Options](#pager-query-options), [BatchGet](#batch-get), [BatchDelete](#batch-write-delete-records), and [BatchPut](#batch-write-put-records). |
2533
+ | originalErr | `false` | By default, **ElectroDB** alters the stacktrace of any exceptions thrown by the DynamoDB client to give better visibility to the developer. Set this value equal to `true` to turn off this functionality and return the error unchanged. |
2534
+ | concurrent | `1` | When performing batch operations, how many requests (1 batch operation == 1 request) to DynamoDB should ElectroDB make at one time. Be mindful of your DynamoDB throughput configurations |
2379
2535
 
2380
2536
  # Errors:
2381
2537
  | Error Code | Description |
@@ -2605,33 +2761,50 @@ Tables can be defined on the [Service Options](#service-options) object when you
2605
2761
  When performing a bulk operation ([Batch Get](#batch-get), [Batch Delete Records](#batch-write-delete-records), [Batch Put Records](#batch-write-put-records)) you can pass a [Query Options](#query-options) called `concurrent`, which impacts how many batch requests can occur at the same time. Your value pass the test of both, `!isNaN(parseInt(value))` and `parseInt(value) > 0`.
2606
2762
 
2607
2763
  *What to do about it:*
2608
- Expect this error only if youre providing a concurrency value. Double check the value you are providing is the value you expect to be passing, and that the value passes the tests listed above.
2764
+ Expect this error only if youre providing a concurrency value. Double check the value you are providing is the value you expect to be passing, and that the value passes the tests listed above.
2765
+
2766
+ ### aws-error
2767
+ *Code: 4001*
2768
+
2769
+ *Why this occurred:*
2770
+ DynamoDB did not like something about your query.
2771
+
2772
+ *What to do about it:*
2773
+ By default ElectroDB tries to keep the stack trace close to your code, ideally this can help you identify what might be going on. A tip to help with troubleshooting: use `.params()` to get more insight into how your query is converted to DocClient params.
2774
+
2775
+ ### Unknown Errors
2609
2776
 
2610
2777
  ### Invalid Last Evaluated Key
2611
- *Code: 4002*
2778
+ *Code: 5003*
2612
2779
 
2613
2780
  *Why this occurred:*
2614
2781
  _Likely_ you were were calling `.page()` on a `scan`. If you weren't please make an issue and include as much detail about your query as possible.
2615
-
2782
+
2616
2783
  *What to do about it:*
2617
- It is highly recommended to use the exclusiveStartKeyRaw flag when using .page() with scans. This is because when using scan on large tables the docClient may return an ExclusiveStartKey for a record that does not belong to entity making the query (regardless of the filters set). In these cases ElectroDB will return null (to avoid leaking the keys of other entities) when further pagination may be needed to find your records.
2784
+ It is highly recommended to use the query option, `{pager: "raw"}`, when using the .page() method with *scans*. This is because when using scan on large tables the docClient may return an ExclusiveStartKey for a record that does not belong to entity making the query (regardless of the filters set). In these cases ElectroDB will return null (to avoid leaking the keys of other entities) when further pagination may be needed to find your records.
2618
2785
  ```javascript
2619
2786
  // example
2620
- model.scan.page({exclusiveStartKeyRaw: true});
2787
+ myModel.scan.page(null, {pager: "raw"});
2621
2788
  ```
2622
2789
 
2623
- ### aws-error
2624
- *Code: 4001*
2790
+ ### No Owner For Pager
2791
+ *Code: 5004*
2625
2792
 
2626
2793
  *Why this occurred:*
2627
- DynamoDB did not like something about your query.
2628
-
2794
+ When using pagination with a Service, ElectroDB will try to identify which Entity is associated with the supplied pager. This error can occur when you supply an invalid pager, or when you are using a different [pager option](#pager-query-options) to a pager than what was used when retrieving it. Consult the section on [Pagination](#page) to learn more.
2795
+
2629
2796
  *What to do about it:*
2630
- By default ElectroDB tries to keep the stack trace close to your code, ideally this can help you identify what might be going on. A tip to help with troubleshooting: use `.params()` to get more insight into how your query is converted to DocClient params.
2797
+ If you are sure the pager you are passing to `.page()` is the same you received from `.page()` this could be an unexpected error. To mitigate the issue use the Query Option `{pager: "raw"}` and please open a support issue.
2631
2798
 
2632
-
2799
+ ### Pager Not Unique
2800
+
2801
+ *Code: 5005*
2633
2802
 
2634
- ### Unknown Error
2803
+ *Why this occurred:*
2804
+ When using pagination with a Service, ElectroDB will try to identify which Entity is associated with the supplied [pager option](#pager-query-options). This error can occur when you supply a pager that resolves to more than one Entity. This can happen if your entities share the same facets for the index you are querying on, and you are using the Query Option `{pager: "item""}`.
2805
+
2806
+ *What to do about it:*
2807
+ Because this scenario is possible with otherwise well considered/thoughtful entity models, the default `pager` type used by ElectroDB is `"named"`. To avoid this error, you will need to use either the `"raw"` or `"named"` [pager options](#pager-query-options) for any index that could result in an ambiguous Entity owner.
2635
2808
 
2636
2809
  # Examples
2637
2810
 
package/browser.js ADDED
@@ -0,0 +1,53 @@
1
+ const ElectroDB = require("./index");
2
+ window.Prism = window.Prism || {};
3
+ const appDiv = document.getElementById('param-container');
4
+
5
+ function printToScreen(val) {
6
+ const innerHtml = appDiv.innerHTML;
7
+ // if (window.Prism) {
8
+ // console.log("fn", window.Prism.highlight, window.Prism.highlight.toString());
9
+ // console.log("window.Prism.languages.json", Object.keys(window.Prism.languages), window.Prism.languages.JSON);
10
+ // // appDiv.innerHTML = innerHtml + window.Prism.highlight(val, window.Prism.languages.json, 'json');
11
+ // } else {
12
+ // console.log("FUUUU", window, window.Prism);
13
+ // }
14
+ // appDiv.innerHTML = innerHtml + Prism.highlight(val, Prism.languages.json, 'json');
15
+ appDiv.innerHTML = innerHtml + `<hr><pre><code class="language-json">${val}</code></pre>`;
16
+ }
17
+
18
+ function clearScreen() {
19
+ appDiv.innerHTML = '';
20
+ }
21
+
22
+ class Entity extends ElectroDB.Entity {
23
+ constructor(...params) {
24
+ super(...params);
25
+ this.client = {};
26
+ }
27
+ _queryParams(state, config) {
28
+ const params = super._queryParams(state, config);
29
+ printToScreen(JSON.stringify(params, null, 4));
30
+ return params;
31
+ }
32
+
33
+ _params(state, config) {
34
+ // @ts-ignore
35
+ const params = super._params(state, config);
36
+ printToScreen(JSON.stringify(params, null, 4));
37
+ return params;
38
+ }
39
+
40
+ go(type, params) {
41
+ printToScreen(JSON.stringify(params, null, 4));
42
+ }
43
+ }
44
+
45
+ class Service extends ElectroDB.Service {}
46
+
47
+
48
+ window.ElectroDB = {
49
+ Entity,
50
+ Service,
51
+ printToScreen,
52
+ clearScreen
53
+ };