electrodb 2.1.2 → 2.2.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/.travis.yml +1 -1
- package/README.md +40 -12
- package/index.d.ts +369 -6
- package/output +2299 -0
- package/package.json +9 -7
- package/src/clauses.js +148 -32
- package/src/client.js +2 -2
- package/src/entity.js +258 -90
- package/src/schema.js +45 -17
- 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.1
|
|
3
|
+
"version": "2.2.1",
|
|
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);
|
|
@@ -403,13 +432,22 @@ let clauses = {
|
|
|
403
432
|
}
|
|
404
433
|
try {
|
|
405
434
|
state.addOption('_isPagination', true);
|
|
406
|
-
const
|
|
435
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
407
436
|
return state
|
|
408
437
|
.setMethod(MethodTypes.query)
|
|
409
438
|
.setType(QueryTypes.is)
|
|
410
|
-
.setPK(entity._expectFacets(facets,
|
|
439
|
+
.setPK(entity._expectFacets(facets, pk))
|
|
411
440
|
.ifSK(() => {
|
|
412
|
-
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
|
+
}
|
|
413
451
|
});
|
|
414
452
|
} catch(err) {
|
|
415
453
|
state.setError(err);
|
|
@@ -425,12 +463,15 @@ let clauses = {
|
|
|
425
463
|
return state;
|
|
426
464
|
}
|
|
427
465
|
try {
|
|
428
|
-
const
|
|
466
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
467
|
+
const endingSk = state.identifyCompositeAttributes(endingFacets, sk, pk);
|
|
468
|
+
const startingSk = state.identifyCompositeAttributes(startingFacets, sk, pk);
|
|
429
469
|
return state
|
|
430
470
|
.setType(QueryTypes.and)
|
|
431
|
-
.setSK(
|
|
471
|
+
.setSK(endingSk.composites)
|
|
432
472
|
.setType(QueryTypes.between)
|
|
433
|
-
.setSK(
|
|
473
|
+
.setSK(startingSk.composites)
|
|
474
|
+
.filterProperties(FilterOperationNames.lte, endingSk.composites);
|
|
434
475
|
} catch(err) {
|
|
435
476
|
state.setError(err);
|
|
436
477
|
return state;
|
|
@@ -449,7 +490,7 @@ let clauses = {
|
|
|
449
490
|
.setType(QueryTypes.begins)
|
|
450
491
|
.ifSK(state => {
|
|
451
492
|
const attributes = state.getCompositeAttributes();
|
|
452
|
-
state.setSK(
|
|
493
|
+
state.setSK(state.buildQueryComposites(facets, attributes.sk));
|
|
453
494
|
});
|
|
454
495
|
} catch(err) {
|
|
455
496
|
state.setError(err);
|
|
@@ -465,11 +506,16 @@ let clauses = {
|
|
|
465
506
|
return state;
|
|
466
507
|
}
|
|
467
508
|
try {
|
|
509
|
+
|
|
468
510
|
return state
|
|
469
511
|
.setType(QueryTypes.gt)
|
|
470
512
|
.ifSK(state => {
|
|
471
|
-
const
|
|
472
|
-
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
|
+
});
|
|
473
519
|
});
|
|
474
520
|
} catch(err) {
|
|
475
521
|
state.setError(err);
|
|
@@ -489,7 +535,7 @@ let clauses = {
|
|
|
489
535
|
.setType(QueryTypes.gte)
|
|
490
536
|
.ifSK(state => {
|
|
491
537
|
const attributes = state.getCompositeAttributes();
|
|
492
|
-
state.setSK(
|
|
538
|
+
state.setSK(state.buildQueryComposites(facets, attributes.sk));
|
|
493
539
|
});
|
|
494
540
|
} catch(err) {
|
|
495
541
|
state.setError(err);
|
|
@@ -507,8 +553,9 @@ let clauses = {
|
|
|
507
553
|
try {
|
|
508
554
|
return state.setType(QueryTypes.lt)
|
|
509
555
|
.ifSK(state => {
|
|
510
|
-
const
|
|
511
|
-
state.
|
|
556
|
+
const {pk, sk} = state.getCompositeAttributes();
|
|
557
|
+
const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
|
|
558
|
+
state.setSK(composites);
|
|
512
559
|
});
|
|
513
560
|
} catch(err) {
|
|
514
561
|
state.setError(err);
|
|
@@ -526,8 +573,12 @@ let clauses = {
|
|
|
526
573
|
try {
|
|
527
574
|
return state.setType(QueryTypes.lte)
|
|
528
575
|
.ifSK(state => {
|
|
529
|
-
const
|
|
530
|
-
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
|
+
});
|
|
531
582
|
});
|
|
532
583
|
} catch(err) {
|
|
533
584
|
state.setError(err);
|
|
@@ -543,7 +594,7 @@ let clauses = {
|
|
|
543
594
|
throw state.error;
|
|
544
595
|
}
|
|
545
596
|
try {
|
|
546
|
-
if (!v.isStringHasLength(options.table) && !v.isStringHasLength(entity.
|
|
597
|
+
if (!v.isStringHasLength(options.table) && !v.isStringHasLength(entity.getTableName())) {
|
|
547
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.`);
|
|
548
599
|
}
|
|
549
600
|
const method = state.getMethod();
|
|
@@ -693,6 +744,71 @@ class ChainState {
|
|
|
693
744
|
return this.query.facets;
|
|
694
745
|
}
|
|
695
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
|
+
|
|
696
812
|
setSK(attributes, type = this.query.type) {
|
|
697
813
|
if (this.hasSortKey) {
|
|
698
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
|
+
};
|