electrodb 2.1.1 → 2.2.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/.travis.yml +1 -1
- package/README.md +78 -45
- package/index.d.ts +371 -8
- package/index.js +8 -1
- package/output +2299 -0
- package/package.json +9 -7
- package/src/clauses.js +153 -32
- package/src/client.js +2 -2
- package/src/entity.js +257 -89
- package/src/operations.js +4 -2
- package/src/schema.js +10 -0
- package/src/service.js +145 -29
- package/src/types.js +56 -2
- package/src/util.js +15 -1
- package/src/validations.js +5 -0
- package/src/where.js +14 -18
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electrodb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "npm run test:types && npm run test:unit",
|
|
8
|
-
"test:
|
|
7
|
+
"test": "npm run test:types && npm run test:init && npm run test:unit",
|
|
8
|
+
"test:init": "LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 node ./test/init.js",
|
|
9
|
+
"test:unit": "LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 mocha -r ts-node/register ./test/**.spec.*",
|
|
9
10
|
"test:types": "tsd",
|
|
10
|
-
"coverage": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
|
|
11
|
-
"coverage
|
|
12
|
-
"coverage
|
|
13
|
-
"build:browser": "browserify playground/browser.js -o playground/bundle.js"
|
|
11
|
+
"coverage": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
|
|
12
|
+
"coverage:local:coveralls": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
|
|
13
|
+
"coverage:local:html": "npm run test:init && nyc npm run test:unit && nyc report --reporter=html",
|
|
14
|
+
"build:browser": "browserify playground/browser.js -o playground/bundle.js",
|
|
15
|
+
"build": "sh buildbrowser.sh"
|
|
14
16
|
},
|
|
15
17
|
"repository": {
|
|
16
18
|
"type": "git",
|
package/src/clauses.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { QueryTypes, MethodTypes, ItemOperations, ExpressionTypes, TableIndex, TerminalOperation, KeyTypes } = require("./types");
|
|
1
|
+
const { QueryTypes, MethodTypes, ItemOperations, ExpressionTypes, TableIndex, TerminalOperation, KeyTypes, IndexTypes } = require("./types");
|
|
2
2
|
const {AttributeOperationProxy, UpdateOperations, FilterOperationNames} = require("./operations");
|
|
3
3
|
const {UpdateExpression} = require("./update");
|
|
4
4
|
const {FilterExpression} = require("./where");
|
|
@@ -28,7 +28,36 @@ function batchAction(action, type, entity, state, payload) {
|
|
|
28
28
|
let clauses = {
|
|
29
29
|
index: {
|
|
30
30
|
name: "index",
|
|
31
|
-
children: ["get", "delete", "update", "query", "put", "scan", "collection", "create", "remove", "patch", "batchPut", "batchDelete", "batchGet"],
|
|
31
|
+
children: ["get", "delete", "update", "query", "put", "scan", "collection", "clusteredCollection", "create", "remove", "patch", "batchPut", "batchDelete", "batchGet"],
|
|
32
|
+
},
|
|
33
|
+
clusteredCollection: {
|
|
34
|
+
name: "clusteredCollection",
|
|
35
|
+
action(entity, state, collection = "", facets /* istanbul ignore next */ = {}) {
|
|
36
|
+
if (state.getError() !== null) {
|
|
37
|
+
return state;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
41
|
+
return state
|
|
42
|
+
.setType(QueryTypes.clustered_collection)
|
|
43
|
+
.setMethod(MethodTypes.query)
|
|
44
|
+
.setCollection(collection)
|
|
45
|
+
.setPK(entity._expectFacets(facets, pk))
|
|
46
|
+
.ifSK(() => {
|
|
47
|
+
const {composites, unused} = state.identifyCompositeAttributes(facets, sk, pk);
|
|
48
|
+
state.setSK(composites);
|
|
49
|
+
// we must apply eq on filter on all provided because if the user then does a sort key operation, it'd actually then unexpect results
|
|
50
|
+
if (sk.length > 1) {
|
|
51
|
+
state.filterProperties(FilterOperationNames.eq, {...unused, ...composites});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
} catch(err) {
|
|
56
|
+
state.setError(err);
|
|
57
|
+
return state;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
children: ["between", "gte", "gt", "lte", "lt", "begins", "params", "go"],
|
|
32
61
|
},
|
|
33
62
|
collection: {
|
|
34
63
|
name: "collection",
|
|
@@ -38,13 +67,12 @@ let clauses = {
|
|
|
38
67
|
return state;
|
|
39
68
|
}
|
|
40
69
|
try {
|
|
41
|
-
const {pk} = state.getCompositeAttributes();
|
|
70
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
42
71
|
return state
|
|
43
72
|
.setType(QueryTypes.collection)
|
|
44
73
|
.setMethod(MethodTypes.query)
|
|
45
74
|
.setCollection(collection)
|
|
46
75
|
.setPK(entity._expectFacets(facets, pk));
|
|
47
|
-
|
|
48
76
|
} catch(err) {
|
|
49
77
|
state.setError(err);
|
|
50
78
|
return state;
|
|
@@ -75,14 +103,15 @@ let clauses = {
|
|
|
75
103
|
return state;
|
|
76
104
|
}
|
|
77
105
|
try {
|
|
78
|
-
const
|
|
106
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
107
|
+
const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
|
|
79
108
|
return state
|
|
80
109
|
.setMethod(MethodTypes.get)
|
|
81
110
|
.setType(QueryTypes.eq)
|
|
82
|
-
.setPK(entity._expectFacets(facets,
|
|
111
|
+
.setPK(entity._expectFacets(facets, pk))
|
|
83
112
|
.ifSK(() => {
|
|
84
|
-
entity._expectFacets(facets,
|
|
85
|
-
state.setSK(
|
|
113
|
+
entity._expectFacets(facets, sk);
|
|
114
|
+
state.setSK(composites);
|
|
86
115
|
});
|
|
87
116
|
} catch(err) {
|
|
88
117
|
state.setError(err);
|
|
@@ -109,14 +138,14 @@ let clauses = {
|
|
|
109
138
|
return state;
|
|
110
139
|
}
|
|
111
140
|
try {
|
|
112
|
-
const
|
|
141
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
113
142
|
return state
|
|
114
143
|
.setMethod(MethodTypes.delete)
|
|
115
144
|
.setType(QueryTypes.eq)
|
|
116
|
-
.setPK(entity._expectFacets(facets,
|
|
145
|
+
.setPK(entity._expectFacets(facets, pk))
|
|
117
146
|
.ifSK(() => {
|
|
118
|
-
entity._expectFacets(facets,
|
|
119
|
-
state.setSK(
|
|
147
|
+
entity._expectFacets(facets, sk);
|
|
148
|
+
state.setSK(state.buildQueryComposites(facets, sk));
|
|
120
149
|
});
|
|
121
150
|
} catch(err) {
|
|
122
151
|
state.setError(err);
|
|
@@ -146,7 +175,7 @@ let clauses = {
|
|
|
146
175
|
.setPK(entity._expectFacets(facets, attributes.pk))
|
|
147
176
|
.ifSK(() => {
|
|
148
177
|
entity._expectFacets(facets, attributes.sk);
|
|
149
|
-
state.setSK(
|
|
178
|
+
state.setSK(state.buildQueryComposites(facets, attributes.sk));
|
|
150
179
|
});
|
|
151
180
|
} catch(err) {
|
|
152
181
|
state.setError(err);
|
|
@@ -172,7 +201,7 @@ let clauses = {
|
|
|
172
201
|
.setPK(entity._expectFacets(record, attributes.pk))
|
|
173
202
|
.ifSK(() => {
|
|
174
203
|
entity._expectFacets(record, attributes.sk);
|
|
175
|
-
state.setSK(
|
|
204
|
+
state.setSK(state.buildQueryComposites(record, attributes.sk));
|
|
176
205
|
});
|
|
177
206
|
} catch(err) {
|
|
178
207
|
state.setError(err);
|
|
@@ -208,7 +237,7 @@ let clauses = {
|
|
|
208
237
|
.setPK(entity._expectFacets(record, attributes.pk))
|
|
209
238
|
.ifSK(() => {
|
|
210
239
|
entity._expectFacets(record, attributes.sk);
|
|
211
|
-
state.setSK(
|
|
240
|
+
state.setSK(state.buildQueryComposites(record, attributes.sk));
|
|
212
241
|
});
|
|
213
242
|
} catch(err) {
|
|
214
243
|
state.setError(err);
|
|
@@ -237,7 +266,7 @@ let clauses = {
|
|
|
237
266
|
.setPK(entity._expectFacets(facets, attributes.pk))
|
|
238
267
|
.ifSK(() => {
|
|
239
268
|
entity._expectFacets(facets, attributes.sk);
|
|
240
|
-
state.setSK(
|
|
269
|
+
state.setSK(state.buildQueryComposites(facets, attributes.sk));
|
|
241
270
|
});
|
|
242
271
|
} catch(err) {
|
|
243
272
|
state.setError(err);
|
|
@@ -260,7 +289,7 @@ let clauses = {
|
|
|
260
289
|
.setPK(entity._expectFacets(facets, attributes.pk))
|
|
261
290
|
.ifSK(() => {
|
|
262
291
|
entity._expectFacets(facets, attributes.sk);
|
|
263
|
-
state.setSK(
|
|
292
|
+
state.setSK(state.buildQueryComposites(facets, attributes.sk));
|
|
264
293
|
});
|
|
265
294
|
} catch(err) {
|
|
266
295
|
state.setError(err);
|
|
@@ -277,6 +306,11 @@ let clauses = {
|
|
|
277
306
|
}
|
|
278
307
|
try {
|
|
279
308
|
state.query.updateProxy.invokeCallback(cb);
|
|
309
|
+
for (const path of Object.keys(state.query.update.refs)) {
|
|
310
|
+
const operation = state.query.update.impacted[path];
|
|
311
|
+
const attribute = state.query.update.refs[path];
|
|
312
|
+
entity.model.schema.checkOperation(attribute, operation);
|
|
313
|
+
}
|
|
280
314
|
return state;
|
|
281
315
|
} catch(err) {
|
|
282
316
|
state.setError(err);
|
|
@@ -398,13 +432,22 @@ let clauses = {
|
|
|
398
432
|
}
|
|
399
433
|
try {
|
|
400
434
|
state.addOption('_isPagination', true);
|
|
401
|
-
const
|
|
435
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
402
436
|
return state
|
|
403
437
|
.setMethod(MethodTypes.query)
|
|
404
438
|
.setType(QueryTypes.is)
|
|
405
|
-
.setPK(entity._expectFacets(facets,
|
|
439
|
+
.setPK(entity._expectFacets(facets, pk))
|
|
406
440
|
.ifSK(() => {
|
|
407
|
-
state.
|
|
441
|
+
const {composites, unused} = state.identifyCompositeAttributes(facets, sk, pk);
|
|
442
|
+
state.setSK(state.buildQueryComposites(facets, sk));
|
|
443
|
+
// we must apply eq on filter on all provided because if the user then does a sort key operation, it'd actually then unexpect results
|
|
444
|
+
if (sk.length > 1) {
|
|
445
|
+
state.filterProperties(FilterOperationNames.eq, {...unused, ...composites});
|
|
446
|
+
}
|
|
447
|
+
if (state.query.options.indexType === IndexTypes.clustered && Object.keys(composites).length < sk.length) {
|
|
448
|
+
state.unsafeApplyFilter(FilterOperationNames.eq, entity.identifiers.entity, entity.getName())
|
|
449
|
+
.unsafeApplyFilter(FilterOperationNames.eq, entity.identifiers.version, entity.getVersion());
|
|
450
|
+
}
|
|
408
451
|
});
|
|
409
452
|
} catch(err) {
|
|
410
453
|
state.setError(err);
|
|
@@ -420,12 +463,15 @@ let clauses = {
|
|
|
420
463
|
return state;
|
|
421
464
|
}
|
|
422
465
|
try {
|
|
423
|
-
const
|
|
466
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
467
|
+
const endingSk = state.identifyCompositeAttributes(endingFacets, sk, pk);
|
|
468
|
+
const startingSk = state.identifyCompositeAttributes(startingFacets, sk, pk);
|
|
424
469
|
return state
|
|
425
470
|
.setType(QueryTypes.and)
|
|
426
|
-
.setSK(
|
|
471
|
+
.setSK(endingSk.composites)
|
|
427
472
|
.setType(QueryTypes.between)
|
|
428
|
-
.setSK(
|
|
473
|
+
.setSK(startingSk.composites)
|
|
474
|
+
.filterProperties(FilterOperationNames.lte, endingSk.composites);
|
|
429
475
|
} catch(err) {
|
|
430
476
|
state.setError(err);
|
|
431
477
|
return state;
|
|
@@ -444,7 +490,7 @@ let clauses = {
|
|
|
444
490
|
.setType(QueryTypes.begins)
|
|
445
491
|
.ifSK(state => {
|
|
446
492
|
const attributes = state.getCompositeAttributes();
|
|
447
|
-
state.setSK(
|
|
493
|
+
state.setSK(state.buildQueryComposites(facets, attributes.sk));
|
|
448
494
|
});
|
|
449
495
|
} catch(err) {
|
|
450
496
|
state.setError(err);
|
|
@@ -460,11 +506,16 @@ let clauses = {
|
|
|
460
506
|
return state;
|
|
461
507
|
}
|
|
462
508
|
try {
|
|
509
|
+
|
|
463
510
|
return state
|
|
464
511
|
.setType(QueryTypes.gt)
|
|
465
512
|
.ifSK(state => {
|
|
466
|
-
const
|
|
467
|
-
state.
|
|
513
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
514
|
+
const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
|
|
515
|
+
state.setSK(composites);
|
|
516
|
+
state.filterProperties(FilterOperationNames.gt, {
|
|
517
|
+
...composites,
|
|
518
|
+
});
|
|
468
519
|
});
|
|
469
520
|
} catch(err) {
|
|
470
521
|
state.setError(err);
|
|
@@ -484,7 +535,7 @@ let clauses = {
|
|
|
484
535
|
.setType(QueryTypes.gte)
|
|
485
536
|
.ifSK(state => {
|
|
486
537
|
const attributes = state.getCompositeAttributes();
|
|
487
|
-
state.setSK(
|
|
538
|
+
state.setSK(state.buildQueryComposites(facets, attributes.sk));
|
|
488
539
|
});
|
|
489
540
|
} catch(err) {
|
|
490
541
|
state.setError(err);
|
|
@@ -502,8 +553,9 @@ let clauses = {
|
|
|
502
553
|
try {
|
|
503
554
|
return state.setType(QueryTypes.lt)
|
|
504
555
|
.ifSK(state => {
|
|
505
|
-
const
|
|
506
|
-
state.
|
|
556
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
557
|
+
const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
|
|
558
|
+
state.setSK(composites);
|
|
507
559
|
});
|
|
508
560
|
} catch(err) {
|
|
509
561
|
state.setError(err);
|
|
@@ -521,8 +573,12 @@ let clauses = {
|
|
|
521
573
|
try {
|
|
522
574
|
return state.setType(QueryTypes.lte)
|
|
523
575
|
.ifSK(state => {
|
|
524
|
-
const
|
|
525
|
-
state.
|
|
576
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
577
|
+
const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
|
|
578
|
+
state.setSK(composites);
|
|
579
|
+
state.filterProperties(FilterOperationNames.lte, {
|
|
580
|
+
...composites,
|
|
581
|
+
});
|
|
526
582
|
});
|
|
527
583
|
} catch(err) {
|
|
528
584
|
state.setError(err);
|
|
@@ -538,7 +594,7 @@ let clauses = {
|
|
|
538
594
|
throw state.error;
|
|
539
595
|
}
|
|
540
596
|
try {
|
|
541
|
-
if (!v.isStringHasLength(options.table) && !v.isStringHasLength(entity.
|
|
597
|
+
if (!v.isStringHasLength(options.table) && !v.isStringHasLength(entity.getTableName())) {
|
|
542
598
|
throw new e.ElectroError(e.ErrorCodes.MissingTable, `Table name not defined. Table names must be either defined on the model, instance configuration, or as a query option.`);
|
|
543
599
|
}
|
|
544
600
|
const method = state.getMethod();
|
|
@@ -688,6 +744,71 @@ class ChainState {
|
|
|
688
744
|
return this.query.facets;
|
|
689
745
|
}
|
|
690
746
|
|
|
747
|
+
buildQueryComposites(provided, definition) {
|
|
748
|
+
return definition
|
|
749
|
+
.map(name => [name, provided[name]])
|
|
750
|
+
.reduce(
|
|
751
|
+
(result, [name, value]) => {
|
|
752
|
+
if (value !== undefined) {
|
|
753
|
+
result[name] = value;
|
|
754
|
+
}
|
|
755
|
+
return result;
|
|
756
|
+
},
|
|
757
|
+
{},
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
identifyCompositeAttributes(provided, defined, skip) {
|
|
762
|
+
// todo: make sure attributes are valid
|
|
763
|
+
const composites = {};
|
|
764
|
+
const unused = {};
|
|
765
|
+
const definedSet = new Set(defined || []);
|
|
766
|
+
const skipSet = new Set(skip || []);
|
|
767
|
+
for (const key of Object.keys(provided)) {
|
|
768
|
+
const value = provided[key];
|
|
769
|
+
if (definedSet.has(key)) {
|
|
770
|
+
composites[key] = value;
|
|
771
|
+
} else if (skipSet.has(key)) {
|
|
772
|
+
continue;
|
|
773
|
+
} else {
|
|
774
|
+
unused[key] = value;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return {
|
|
779
|
+
composites,
|
|
780
|
+
unused,
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
applyFilter(operation, name, ...values) {
|
|
785
|
+
if (FilterOperationNames[operation] !== undefined & name !== undefined && values.length > 0) {
|
|
786
|
+
const attribute = this.attributes[name];
|
|
787
|
+
if (attribute !== undefined) {
|
|
788
|
+
this.unsafeApplyFilter(operation, attribute.field, ...values);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return this;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
unsafeApplyFilter(operation, name, ...values) {
|
|
795
|
+
if (FilterOperationNames[operation] !== undefined & name !== undefined && values.length > 0) {
|
|
796
|
+
const filter = this.query.filter[ExpressionTypes.FilterExpression];
|
|
797
|
+
filter.unsafeSet(operation, name, ...values);
|
|
798
|
+
}
|
|
799
|
+
return this;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
filterProperties(operation, obj = {}) {
|
|
803
|
+
for (const property in obj) {
|
|
804
|
+
const value = obj[property];
|
|
805
|
+
if (value !== undefined) {
|
|
806
|
+
this.applyFilter(operation, property, value);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return this;
|
|
810
|
+
}
|
|
811
|
+
|
|
691
812
|
setSK(attributes, type = this.query.type) {
|
|
692
813
|
if (this.hasSortKey) {
|
|
693
814
|
this.query.keys.sk.push({
|
package/src/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
const lib = require('@aws-sdk/lib-dynamodb')
|
|
1
2
|
const { isFunction } = require('./validations');
|
|
2
3
|
const { ElectroError, ErrorCodes } = require('./errors');
|
|
3
|
-
const lib = require('@aws-sdk/lib-dynamodb');
|
|
4
4
|
|
|
5
5
|
const DocumentClientVersions = {
|
|
6
6
|
v2: 'v2',
|
|
@@ -144,4 +144,4 @@ module.exports = {
|
|
|
144
144
|
DocumentClientVersions,
|
|
145
145
|
supportedClientVersions,
|
|
146
146
|
DocumentClientV3Wrapper,
|
|
147
|
-
};
|
|
147
|
+
};
|