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/src/schema.js
CHANGED
|
@@ -524,15 +524,22 @@ class MapAttribute extends Attribute {
|
|
|
524
524
|
traverser: this.traverser
|
|
525
525
|
});
|
|
526
526
|
this.properties = properties;
|
|
527
|
+
this.isRoot = !!definition.isRoot;
|
|
527
528
|
this.get = this._makeGet(definition.get, properties);
|
|
528
529
|
this.set = this._makeSet(definition.set, properties);
|
|
529
530
|
}
|
|
530
531
|
|
|
531
532
|
_makeGet(get, properties) {
|
|
532
533
|
this._checkGetSet(get, "get");
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
534
|
+
const getter = get || ((val) => {
|
|
535
|
+
const isEmpty = !val || Object.keys(val).length === 0;
|
|
536
|
+
const isNotRequired = !this.required;
|
|
537
|
+
const isRoot = this.isRoot;
|
|
538
|
+
if (isEmpty && isRoot && !isNotRequired) {
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
return val;
|
|
542
|
+
});
|
|
536
543
|
return (values, siblings) => {
|
|
537
544
|
const data = {};
|
|
538
545
|
|
|
@@ -541,6 +548,9 @@ class MapAttribute extends Attribute {
|
|
|
541
548
|
}
|
|
542
549
|
|
|
543
550
|
if (values === undefined) {
|
|
551
|
+
if (!get) {
|
|
552
|
+
return undefined;
|
|
553
|
+
}
|
|
544
554
|
return getter(data, siblings);
|
|
545
555
|
}
|
|
546
556
|
|
|
@@ -561,11 +571,23 @@ class MapAttribute extends Attribute {
|
|
|
561
571
|
|
|
562
572
|
_makeSet(set, properties) {
|
|
563
573
|
this._checkGetSet(set, "set");
|
|
564
|
-
const setter = set || ((
|
|
574
|
+
const setter = set || ((val) => {
|
|
575
|
+
const isEmpty = !val || Object.keys(val).length === 0;
|
|
576
|
+
const isNotRequired = !this.required;
|
|
577
|
+
const isRoot = this.isRoot;
|
|
578
|
+
if (isEmpty && isRoot && !isNotRequired) {
|
|
579
|
+
return undefined;
|
|
580
|
+
}
|
|
581
|
+
return val;
|
|
582
|
+
});
|
|
583
|
+
|
|
565
584
|
return (values, siblings) => {
|
|
566
585
|
const data = {};
|
|
567
586
|
if (values === undefined) {
|
|
568
|
-
|
|
587
|
+
if (!set) {
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
590
|
+
return setter(values, siblings);
|
|
569
591
|
}
|
|
570
592
|
for (const name of Object.keys(properties.attributes)) {
|
|
571
593
|
const attribute = properties.attributes[name];
|
|
@@ -624,18 +646,18 @@ class MapAttribute extends Attribute {
|
|
|
624
646
|
}
|
|
625
647
|
|
|
626
648
|
val(value) {
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
649
|
+
const incomingIsEmpty = value === undefined;
|
|
650
|
+
let fromDefault = false;
|
|
651
|
+
let data;
|
|
652
|
+
if (value === undefined) {
|
|
653
|
+
data = this.default();
|
|
654
|
+
if (data !== undefined) {
|
|
655
|
+
fromDefault = true;
|
|
631
656
|
}
|
|
632
|
-
|
|
657
|
+
} else {
|
|
658
|
+
data = value;
|
|
633
659
|
}
|
|
634
660
|
|
|
635
|
-
let data = value === undefined
|
|
636
|
-
? getValue(value)
|
|
637
|
-
: value;
|
|
638
|
-
|
|
639
661
|
const valueType = getValueType(data);
|
|
640
662
|
|
|
641
663
|
if (data === undefined) {
|
|
@@ -654,6 +676,10 @@ class MapAttribute extends Attribute {
|
|
|
654
676
|
}
|
|
655
677
|
}
|
|
656
678
|
|
|
679
|
+
if (Object.keys(response).length === 0 && !fromDefault && this.isRoot && !this.required && incomingIsEmpty) {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
|
|
657
683
|
return response;
|
|
658
684
|
}
|
|
659
685
|
}
|
|
@@ -959,9 +985,9 @@ class SetAttribute extends Attribute {
|
|
|
959
985
|
}
|
|
960
986
|
|
|
961
987
|
class Schema {
|
|
962
|
-
constructor(properties = {}, facets = {}, {traverser = new AttributeTraverser(), client, parent} = {}) {
|
|
988
|
+
constructor(properties = {}, facets = {}, {traverser = new AttributeTraverser(), client, parent, isRoot} = {}) {
|
|
963
989
|
this._validateProperties(properties, parent);
|
|
964
|
-
let schema = Schema.normalizeAttributes(properties, facets, {traverser, client, parent});
|
|
990
|
+
let schema = Schema.normalizeAttributes(properties, facets, {traverser, client, parent, isRoot});
|
|
965
991
|
this.client = client;
|
|
966
992
|
this.attributes = schema.attributes;
|
|
967
993
|
this.enums = schema.enums;
|
|
@@ -972,9 +998,10 @@ class Schema {
|
|
|
972
998
|
this.requiredAttributes = schema.requiredAttributes;
|
|
973
999
|
this.translationForWatching = this._formatWatchTranslations(this.attributes);
|
|
974
1000
|
this.traverser = traverser;
|
|
1001
|
+
this.isRoot = !!isRoot;
|
|
975
1002
|
}
|
|
976
1003
|
|
|
977
|
-
static normalizeAttributes(attributes = {}, facets = {}, {traverser, client, parent} = {}) {
|
|
1004
|
+
static normalizeAttributes(attributes = {}, facets = {}, {traverser, client, parent, isRoot} = {}) {
|
|
978
1005
|
const attributeHasParent = !!parent;
|
|
979
1006
|
let invalidProperties = [];
|
|
980
1007
|
let normalized = {};
|
|
@@ -1073,6 +1100,7 @@ class Schema {
|
|
|
1073
1100
|
postfix,
|
|
1074
1101
|
traverser,
|
|
1075
1102
|
isKeyField,
|
|
1103
|
+
isRoot: !!isRoot,
|
|
1076
1104
|
label: attribute.label,
|
|
1077
1105
|
required: !!attribute.required,
|
|
1078
1106
|
default: attribute.default,
|
package/src/service.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { Entity } = require("./entity");
|
|
2
2
|
const { clauses } = require("./clauses");
|
|
3
|
-
const { KeyCasing, ServiceVersions, Pager, ElectroInstance, ElectroInstanceTypes, ModelVersions } = require("./types");
|
|
3
|
+
const { KeyCasing, ServiceVersions, Pager, ElectroInstance, ElectroInstanceTypes, ModelVersions, IndexTypes } = require("./types");
|
|
4
4
|
const { FilterFactory } = require("./filters");
|
|
5
5
|
const { FilterOperations } = require("./operations");
|
|
6
6
|
const { WhereFactory } = require("./where");
|
|
@@ -65,7 +65,9 @@ class Service {
|
|
|
65
65
|
this.entities = {};
|
|
66
66
|
this.find = {};
|
|
67
67
|
this.collectionSchema = {};
|
|
68
|
+
this.compositeAttributes = {};
|
|
68
69
|
this.collections = {};
|
|
70
|
+
this.identifiers = {};
|
|
69
71
|
this._instance = ElectroInstance.service;
|
|
70
72
|
this._instanceType = ElectroInstanceTypes.service;
|
|
71
73
|
}
|
|
@@ -89,7 +91,9 @@ class Service {
|
|
|
89
91
|
this.entities = {};
|
|
90
92
|
this.find = {};
|
|
91
93
|
this.collectionSchema = {};
|
|
94
|
+
this.compositeAttributes = {};
|
|
92
95
|
this.collections = {};
|
|
96
|
+
this.identifiers = {};
|
|
93
97
|
this._instance = ElectroInstance.service;
|
|
94
98
|
this._instanceType = ElectroInstanceTypes.service;
|
|
95
99
|
}
|
|
@@ -183,12 +187,12 @@ class Service {
|
|
|
183
187
|
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Service name defined on joined instance, ${entity.model.service}, does not match the name of this Service: ${this.service.name}. Verify or update the service name on the Entity/Model to match the name defined on this service.`);
|
|
184
188
|
}
|
|
185
189
|
|
|
186
|
-
if (this.
|
|
187
|
-
entity.
|
|
190
|
+
if (this.getTableName()) {
|
|
191
|
+
entity.setTableName(this.getTableName());
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
if (options.client) {
|
|
191
|
-
entity.
|
|
195
|
+
entity.setClient(options.client);
|
|
192
196
|
}
|
|
193
197
|
|
|
194
198
|
if (options.logger) {
|
|
@@ -205,20 +209,63 @@ class Service {
|
|
|
205
209
|
|
|
206
210
|
this.entities[name] = entity;
|
|
207
211
|
for (let collection of this.entities[name].model.collections) {
|
|
212
|
+
// todo: this used to be inside the collection callback, it does not do well being ran multiple times
|
|
213
|
+
// this forlook adds the entity filters multiple times
|
|
208
214
|
this._addCollectionEntity(collection, name, this.entities[name]);
|
|
209
215
|
this.collections[collection] = (...facets) => {
|
|
210
|
-
|
|
211
|
-
|
|
216
|
+
return this._makeCollectionChain({
|
|
217
|
+
name: collection,
|
|
218
|
+
initialClauses: clauses,
|
|
219
|
+
}, ...facets);
|
|
212
220
|
};
|
|
213
221
|
}
|
|
222
|
+
for (const collection in this.collectionSchema) {
|
|
223
|
+
const collectionSchema = this.collectionSchema[collection];
|
|
224
|
+
this.compositeAttributes[collection] = this._collectionSchemaToCompositeAttributes(collectionSchema);
|
|
225
|
+
}
|
|
214
226
|
this.find = { ...this.entities, ...this.collections };
|
|
215
227
|
return this;
|
|
216
228
|
}
|
|
217
229
|
|
|
218
|
-
|
|
230
|
+
_collectionSchemaToCompositeAttributes(schema) {
|
|
231
|
+
const keys = schema.keys;
|
|
232
|
+
return {
|
|
233
|
+
hasSortKeys: keys.hasSk,
|
|
234
|
+
customFacets: {
|
|
235
|
+
pk: keys.pk.isCustom,
|
|
236
|
+
sk: keys.sk.isCustom,
|
|
237
|
+
},
|
|
238
|
+
pk: keys.pk.facets,
|
|
239
|
+
sk: keys.sk.facets,
|
|
240
|
+
all: [
|
|
241
|
+
...keys.pk.facets.map(name => {
|
|
242
|
+
return {
|
|
243
|
+
name,
|
|
244
|
+
index: keys.index,
|
|
245
|
+
type: 'pk',
|
|
246
|
+
};
|
|
247
|
+
}),
|
|
248
|
+
...keys.sk.facets.map(name => {
|
|
249
|
+
return {
|
|
250
|
+
name,
|
|
251
|
+
index: keys.index,
|
|
252
|
+
type: 'sk',
|
|
253
|
+
};
|
|
254
|
+
})
|
|
255
|
+
],
|
|
256
|
+
collection: keys.collection,
|
|
257
|
+
hasSubCollections: schema.hasSubCollections,
|
|
258
|
+
casing: {
|
|
259
|
+
pk: keys.pk.casing,
|
|
260
|
+
sk: keys.sk.casing,
|
|
261
|
+
},
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
setClient(client) {
|
|
219
266
|
if (client !== undefined) {
|
|
220
267
|
for (let entity of Object.values(this.entities)) {
|
|
221
|
-
entity.
|
|
268
|
+
entity.setClient(client);
|
|
222
269
|
}
|
|
223
270
|
}
|
|
224
271
|
}
|
|
@@ -275,8 +322,9 @@ class Service {
|
|
|
275
322
|
}
|
|
276
323
|
|
|
277
324
|
findKeyOwner(lastEvaluatedKey) {
|
|
278
|
-
return Object.values(this.entities)
|
|
279
|
-
|
|
325
|
+
return Object.values(this.entities)[0];
|
|
326
|
+
// return Object.values(this.entities)
|
|
327
|
+
// .find((entity) => entity.ownsLastEvaluatedKey(lastEvaluatedKey));
|
|
280
328
|
}
|
|
281
329
|
|
|
282
330
|
expectKeyOwner(lastEvaluatedKey) {
|
|
@@ -288,8 +336,9 @@ class Service {
|
|
|
288
336
|
}
|
|
289
337
|
|
|
290
338
|
findCursorOwner(cursor) {
|
|
291
|
-
return Object.values(this.entities)
|
|
292
|
-
|
|
339
|
+
return Object.values(this.entities)[0];
|
|
340
|
+
// return Object.values(this.entities)
|
|
341
|
+
// .find(entity => entity.ownsCursor(cursor));
|
|
293
342
|
}
|
|
294
343
|
|
|
295
344
|
expectCursorOwner(cursor) {
|
|
@@ -300,18 +349,26 @@ class Service {
|
|
|
300
349
|
return owner;
|
|
301
350
|
}
|
|
302
351
|
|
|
303
|
-
|
|
352
|
+
getTableName() {
|
|
304
353
|
return this.service.table;
|
|
305
354
|
}
|
|
306
355
|
|
|
307
|
-
|
|
356
|
+
setTableName(table) {
|
|
308
357
|
this.service.table = table;
|
|
309
358
|
for (let entity of Object.values(this.entities)) {
|
|
310
|
-
entity.
|
|
359
|
+
entity.setTableName(table);
|
|
311
360
|
}
|
|
312
361
|
}
|
|
313
362
|
|
|
314
|
-
_makeCollectionChain(
|
|
363
|
+
_makeCollectionChain({
|
|
364
|
+
name = "",
|
|
365
|
+
initialClauses = {},
|
|
366
|
+
}, facets = {}) {
|
|
367
|
+
const { entities, attributes, identifiers, indexType } = this.collectionSchema[name];
|
|
368
|
+
const compositeAttributes = this.compositeAttributes[name];
|
|
369
|
+
const allEntities = Object.values(entities);
|
|
370
|
+
const entity = allEntities[0];
|
|
371
|
+
|
|
315
372
|
let filterBuilder = new FilterFactory(attributes, FilterOperations);
|
|
316
373
|
let whereBuilder = new WhereFactory(attributes, FilterOperations);
|
|
317
374
|
let clauses = {...initialClauses};
|
|
@@ -319,6 +376,8 @@ class Service {
|
|
|
319
376
|
clauses = filterBuilder.injectFilterClauses(clauses);
|
|
320
377
|
clauses = whereBuilder.injectWhereClauses(clauses);
|
|
321
378
|
|
|
379
|
+
const expression = identifiers.expression || "";
|
|
380
|
+
|
|
322
381
|
let options = {
|
|
323
382
|
// expressions, // DynamoDB doesnt return what I expect it would when provided with these entity filters
|
|
324
383
|
parse: (options, data) => {
|
|
@@ -329,9 +388,20 @@ class Service {
|
|
|
329
388
|
return this.expectKeyOwner(key).serializeCursor(key);
|
|
330
389
|
},
|
|
331
390
|
deserialize: (cursor) => {
|
|
332
|
-
return this.expectCursorOwner(cursor).
|
|
391
|
+
return this.expectCursorOwner(cursor).deserializeCursor(cursor);
|
|
333
392
|
}
|
|
334
|
-
}
|
|
393
|
+
},
|
|
394
|
+
expressions: {
|
|
395
|
+
names: identifiers.names || {},
|
|
396
|
+
values: identifiers.values || {},
|
|
397
|
+
expression: allEntities.length > 1
|
|
398
|
+
? `(${expression})`
|
|
399
|
+
: expression
|
|
400
|
+
},
|
|
401
|
+
attributes,
|
|
402
|
+
entities,
|
|
403
|
+
indexType,
|
|
404
|
+
compositeAttributes,
|
|
335
405
|
};
|
|
336
406
|
|
|
337
407
|
return entity.collection(name, clauses, facets, options);
|
|
@@ -477,7 +547,33 @@ class Service {
|
|
|
477
547
|
if (invalidDefinition) {
|
|
478
548
|
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Validation Error while joining entity, "${name}". ${invalidIndexMessages.join(", ")}`);
|
|
479
549
|
}
|
|
480
|
-
|
|
550
|
+
const sharedSortKeyAttributes = [];
|
|
551
|
+
const sharedSortKeyCompositeAttributeLabels = [];
|
|
552
|
+
const sharedSortKeyLabels = [];
|
|
553
|
+
if (providedIndex.hasSk && definition.hasSk && Array.isArray(definition.sk.labels)) {
|
|
554
|
+
for (let i = 0; i < definition.sk.labels.length; i++) {
|
|
555
|
+
const providedLabels = providedIndex.sk.labels[i];
|
|
556
|
+
const definedLabels = definition.sk.labels[i];
|
|
557
|
+
|
|
558
|
+
const namesMatch = providedLabels && providedLabels.name === definedLabels.name;
|
|
559
|
+
const labelsMatch = providedLabels && providedLabels.label === definedLabels.label;
|
|
560
|
+
if (!namesMatch || !labelsMatch) {
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
sharedSortKeyLabels.push({...definedLabels});
|
|
564
|
+
sharedSortKeyCompositeAttributeLabels.push({...definition.sk.facetLabels[i]})
|
|
565
|
+
sharedSortKeyAttributes.push(definition.sk.facets[i]);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return {
|
|
569
|
+
...definition,
|
|
570
|
+
sk: {
|
|
571
|
+
...definition.sk,
|
|
572
|
+
facets: sharedSortKeyAttributes,
|
|
573
|
+
facetLabels: sharedSortKeyCompositeAttributeLabels,
|
|
574
|
+
labels: sharedSortKeyLabels,
|
|
575
|
+
}
|
|
576
|
+
};
|
|
481
577
|
}
|
|
482
578
|
|
|
483
579
|
_getEntityIndexFromCollectionName(collection, entity) {
|
|
@@ -506,7 +602,7 @@ class Service {
|
|
|
506
602
|
);
|
|
507
603
|
}
|
|
508
604
|
|
|
509
|
-
_processSubCollections(existing, provided, entityName, collectionName) {
|
|
605
|
+
_processSubCollections(providedType, existing, provided, entityName, collectionName) {
|
|
510
606
|
let existingSubCollections;
|
|
511
607
|
let providedSubCollections;
|
|
512
608
|
if (v.isArrayHasLength(existing)) {
|
|
@@ -520,15 +616,19 @@ class Service {
|
|
|
520
616
|
providedSubCollections = [provided];
|
|
521
617
|
}
|
|
522
618
|
|
|
619
|
+
if (providedSubCollections.length > 1 && providedType === IndexTypes.clustered) {
|
|
620
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Clustered indexes do not support sub-collections. The sub-collection "${collectionName}", on Entity "${entityName}" must be defined as either an individual collection name or the index must be redefined as an isolated cluster`);
|
|
621
|
+
}
|
|
523
622
|
const existingRequiredIndex = existingSubCollections.indexOf(collectionName);
|
|
524
623
|
const providedRequiredIndex = providedSubCollections.indexOf(collectionName);
|
|
525
624
|
if (providedRequiredIndex < 0) {
|
|
526
|
-
throw new
|
|
625
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `The collection definition for Collection "${collectionName}" does not exist on Entity "${entityName}".`);
|
|
527
626
|
}
|
|
528
627
|
if (existingRequiredIndex >= 0 && existingRequiredIndex !== providedRequiredIndex) {
|
|
529
|
-
throw new
|
|
628
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `The collection definition for Collection "${collectionName}", on Entity "${entityName}", does not match the established sub-collection order for this service. The collection name provided in slot ${providedRequiredIndex + 1}, ${providedSubCollections[existingRequiredIndex] === undefined ? '(not found)' : `"${providedSubCollections[existingRequiredIndex]}"`}, on Entity "${entityName}", does not match the established collection name in slot ${existingRequiredIndex + 1}, "${collectionName}". When using sub-collections, all Entities within a Service must must implement the same order for all preceding sub-collections.`);
|
|
530
629
|
}
|
|
531
630
|
let length = Math.max(existingRequiredIndex, providedRequiredIndex);
|
|
631
|
+
|
|
532
632
|
for (let i = 0; i <= length; i++) {
|
|
533
633
|
let existingCollection = existingSubCollections[i];
|
|
534
634
|
let providedCollection = providedSubCollections[i];
|
|
@@ -537,7 +637,7 @@ class Service {
|
|
|
537
637
|
return i;
|
|
538
638
|
}
|
|
539
639
|
if (existingCollection !== providedCollection) {
|
|
540
|
-
throw new
|
|
640
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `The collection definition for Collection "${collectionName}", on Entity "${entityName}", does not match the established sub-collection order for this service. The collection name provided in slot ${i+1}, "${providedCollection}", on Entity "${entityName}", does not match the established collection name in slot ${i + 1}, "${existingCollection}". When using sub-collections, all Entities within a Service must must implement the same order for all preceding sub-collections.`);
|
|
541
641
|
}
|
|
542
642
|
} else if (v.isStringHasLength(providedCollection)) {
|
|
543
643
|
if (providedCollection === collectionName) {
|
|
@@ -564,27 +664,43 @@ class Service {
|
|
|
564
664
|
},
|
|
565
665
|
index: undefined,
|
|
566
666
|
table: "",
|
|
567
|
-
collection: []
|
|
667
|
+
collection: [],
|
|
668
|
+
indexType: undefined,
|
|
669
|
+
hasSubCollections: undefined,
|
|
568
670
|
};
|
|
671
|
+
const providedType = providedIndex.type || IndexTypes.isolated;
|
|
672
|
+
if (this.collectionSchema[collection].indexType === undefined) {
|
|
673
|
+
this.collectionSchema[collection].indexType = providedType;
|
|
674
|
+
} else if (this.collectionSchema[collection].indexType !== providedType) {
|
|
675
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Index type mismatch on collection ${collection}. The entity ${name} defines the index as type ${providedType} while the established type for that index is ${this.collectionSchema[collection].indexType}. Note that when omitted, indexes default to the type "${IndexTypes.isolated}"`);
|
|
676
|
+
}
|
|
569
677
|
if (this.collectionSchema[collection].entities[name] !== undefined) {
|
|
570
678
|
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Entity with name '${name}' has already been joined to this service.`);
|
|
571
679
|
}
|
|
572
680
|
|
|
573
681
|
if (this.collectionSchema[collection].table !== "") {
|
|
574
|
-
if (this.collectionSchema[collection].table !== entity.
|
|
575
|
-
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Entity with name '${name}' is defined to use a different Table than what is defined on other Service Entities and/or the Service itself. Entity '${name}' is defined with table name '${entity.
|
|
682
|
+
if (this.collectionSchema[collection].table !== entity.getTableName()) {
|
|
683
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Entity with name '${name}' is defined to use a different Table than what is defined on other Service Entities and/or the Service itself. Entity '${name}' is defined with table name '${entity.getTableName()}' but the Service has been defined to use table name '${this.collectionSchema[collection].table}'. All Entities in a Service must reference the same DynamoDB table. To ensure all Entities will use the same DynamoDB table, it is possible to apply the property 'table' to the Service constructor's configuration parameter.`);
|
|
576
684
|
}
|
|
577
685
|
} else {
|
|
578
|
-
this.collectionSchema[collection].table = entity.
|
|
686
|
+
this.collectionSchema[collection].table = entity.getTableName();
|
|
579
687
|
}
|
|
688
|
+
|
|
580
689
|
this.collectionSchema[collection].keys = this._processEntityKeys(name, this.collectionSchema[collection].keys, providedIndex);
|
|
581
690
|
this.collectionSchema[collection].attributes = this._processEntityAttributes(name, this.collectionSchema[collection].attributes, entity.model.schema.attributes, this.collectionSchema[collection].keys);
|
|
582
691
|
this.collectionSchema[collection].entities[name] = entity;
|
|
583
692
|
this.collectionSchema[collection].identifiers = this._processEntityIdentifiers(this.collectionSchema[collection].identifiers, entity.getIdentifierExpressions(name));
|
|
584
693
|
this.collectionSchema[collection].index = this._processEntityCollectionIndex(this.collectionSchema[collection].index, providedIndex.index, name, collection);
|
|
585
|
-
let collectionIndex = this._processSubCollections(
|
|
694
|
+
let collectionIndex = this._processSubCollections(
|
|
695
|
+
providedType,
|
|
696
|
+
this.collectionSchema[collection].collection,
|
|
697
|
+
providedIndex.collection,
|
|
698
|
+
name,
|
|
699
|
+
collection
|
|
700
|
+
);
|
|
586
701
|
this.collectionSchema[collection].collection[collectionIndex] = collection;
|
|
587
|
-
|
|
702
|
+
this.collectionSchema[collection].hasSubCollections = this.collectionSchema[collection].hasSubCollections || Array.isArray(providedIndex.collection);
|
|
703
|
+
return this.collectionSchema[collection];
|
|
588
704
|
}
|
|
589
705
|
|
|
590
706
|
_processEntityCollectionIndex(existing, provided, name, collection) {
|
package/src/types.js
CHANGED
|
@@ -18,6 +18,7 @@ const QueryTypes = {
|
|
|
18
18
|
begins: "begins",
|
|
19
19
|
between: "between",
|
|
20
20
|
collection: "collection",
|
|
21
|
+
clustered_collection: 'clustered_collection',
|
|
21
22
|
is: "is"
|
|
22
23
|
};
|
|
23
24
|
|
|
@@ -36,11 +37,62 @@ const MethodTypes = {
|
|
|
36
37
|
batchWrite: "batchWrite"
|
|
37
38
|
};
|
|
38
39
|
|
|
40
|
+
const IndexTypes = {
|
|
41
|
+
isolated: 'isolated',
|
|
42
|
+
clustered: 'clustered',
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
const Comparisons = {
|
|
46
|
+
lte: '<=',
|
|
47
|
+
lt: "<",
|
|
40
48
|
gte: ">=",
|
|
41
|
-
gt:
|
|
42
|
-
|
|
49
|
+
gt: '>'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const PartialComparisons = {
|
|
43
53
|
lt: "<",
|
|
54
|
+
gte: ">=",
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* gt becomes gte and last character of incoming value is shifted up one character code
|
|
58
|
+
* example:
|
|
59
|
+
* sk > '2020-09-05'
|
|
60
|
+
* expected
|
|
61
|
+
* - 2020-09-06@05:05_hero
|
|
62
|
+
* - 2020-10-05@05:05_hero
|
|
63
|
+
* - 2022-02-05@05:05_villian
|
|
64
|
+
* - 2022-06-05@05:05_clown
|
|
65
|
+
* - 2022-09-06@05:05_clown
|
|
66
|
+
* actual (bad - includes all 2020-09-05 records)
|
|
67
|
+
* - 2020-09-05@05:05_hero
|
|
68
|
+
* - 2020-09-06@05:05_hero
|
|
69
|
+
* - 2020-10-05@05:05_hero
|
|
70
|
+
* - 2022-02-05@05:05_villian
|
|
71
|
+
* - 2022-06-05@05:05_clown
|
|
72
|
+
*/
|
|
73
|
+
gt: ">=",
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* lte becomes lt and last character of incoming value is shifted up one character code
|
|
77
|
+
* example:
|
|
78
|
+
* sk >= '2020-09-05'
|
|
79
|
+
* expected
|
|
80
|
+
* - 2012-02-05@05:05_clown
|
|
81
|
+
* - 2015-10-05@05:05_hero
|
|
82
|
+
* - 2017-02-05@05:05_clown
|
|
83
|
+
* - 2017-02-05@05:05_villian
|
|
84
|
+
* - 2020-02-05@05:05_clown
|
|
85
|
+
* - 2020-02-25@05:05_clown
|
|
86
|
+
* - 2020-09-05@05:05_hero
|
|
87
|
+
* actual (bad - missing all 2020-09-05 records)
|
|
88
|
+
* - 2012-02-05@05:05_clown
|
|
89
|
+
* - 2015-10-05@05:05_hero
|
|
90
|
+
* - 2017-02-05@05:05_clown
|
|
91
|
+
* - 2017-02-05@05:05_villian
|
|
92
|
+
* - 2020-02-05@05:05_clown
|
|
93
|
+
* - 2020-02-25@05:05_clown
|
|
94
|
+
*/
|
|
95
|
+
lte: "<",
|
|
44
96
|
};
|
|
45
97
|
|
|
46
98
|
const CastTypes = ["string", "number"];
|
|
@@ -210,6 +262,7 @@ module.exports = {
|
|
|
210
262
|
CastTypes,
|
|
211
263
|
KeyCasing,
|
|
212
264
|
PathTypes,
|
|
265
|
+
IndexTypes,
|
|
213
266
|
QueryTypes,
|
|
214
267
|
ValueTypes,
|
|
215
268
|
TableIndex,
|
|
@@ -229,6 +282,7 @@ module.exports = {
|
|
|
229
282
|
UnprocessedTypes,
|
|
230
283
|
AttributeWildCard,
|
|
231
284
|
TerminalOperation,
|
|
285
|
+
PartialComparisons,
|
|
232
286
|
FormatToReturnValues,
|
|
233
287
|
AttributeProxySymbol,
|
|
234
288
|
ElectroInstanceTypes,
|
package/src/util.js
CHANGED
|
@@ -220,6 +220,19 @@ function removePadding({padding = {}, value = ''} = {}) {
|
|
|
220
220
|
return formatted;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
+
function shiftSortOrder(str = '', codePoint) {
|
|
224
|
+
let newString = '';
|
|
225
|
+
for (let i = 0; i < str.length; i++) {
|
|
226
|
+
const isLast = i === str.length - 1;
|
|
227
|
+
let char = str[i];
|
|
228
|
+
if (isLast) {
|
|
229
|
+
char = String.fromCodePoint(char.codePointAt(0) + codePoint);
|
|
230
|
+
}
|
|
231
|
+
newString += char;
|
|
232
|
+
}
|
|
233
|
+
return newString;
|
|
234
|
+
}
|
|
235
|
+
|
|
223
236
|
module.exports = {
|
|
224
237
|
getUnique,
|
|
225
238
|
batchItems,
|
|
@@ -227,13 +240,14 @@ module.exports = {
|
|
|
227
240
|
removePadding,
|
|
228
241
|
removeFixings,
|
|
229
242
|
parseJSONPath,
|
|
243
|
+
shiftSortOrder,
|
|
230
244
|
getInstanceType,
|
|
231
245
|
getModelVersion,
|
|
232
246
|
formatKeyCasing,
|
|
247
|
+
cursorFormatter,
|
|
233
248
|
genericizeJSONPath,
|
|
234
249
|
commaSeparatedString,
|
|
235
250
|
formatAttributeCasing,
|
|
236
|
-
cursorFormatter,
|
|
237
251
|
applyBetaModelOverrides,
|
|
238
252
|
formatIndexNameForDisplay,
|
|
239
253
|
BatchGetOrderMaintainer,
|
package/src/validations.js
CHANGED
package/src/where.js
CHANGED
|
@@ -50,12 +50,8 @@ class FilterExpression extends ExpressionState {
|
|
|
50
50
|
throw new Error(`Invalid operation: "${operation}". Please report`);
|
|
51
51
|
}
|
|
52
52
|
const names = this.setName({}, name, name);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.setValue(name, value);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const condition = template({}, name.expression, names.prop, ...values);
|
|
53
|
+
const valueExpressions = values.map(value => this.setValue(name, value));
|
|
54
|
+
const condition = template({}, names.expression, names.prop, ...valueExpressions);
|
|
59
55
|
this.add(condition);
|
|
60
56
|
}
|
|
61
57
|
|
|
@@ -70,19 +66,19 @@ class WhereFactory {
|
|
|
70
66
|
this.operations = {...operations};
|
|
71
67
|
}
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
69
|
+
getExpressionType(methodType) {
|
|
70
|
+
switch (methodType) {
|
|
71
|
+
case MethodTypes.put:
|
|
72
|
+
case MethodTypes.create:
|
|
73
|
+
case MethodTypes.update:
|
|
74
|
+
case MethodTypes.patch:
|
|
75
|
+
case MethodTypes.delete:
|
|
76
|
+
case MethodTypes.remove:
|
|
77
|
+
return ExpressionTypes.ConditionExpression
|
|
78
|
+
default:
|
|
79
|
+
return ExpressionTypes.FilterExpression
|
|
85
80
|
}
|
|
81
|
+
}
|
|
86
82
|
|
|
87
83
|
buildClause(cb) {
|
|
88
84
|
if (typeof cb !== "function") {
|