electrodb 2.8.2 → 2.9.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/index.d.ts +10 -0
- package/package.json +5 -6
- package/src/clauses.js +46 -10
- package/src/entity.js +6 -2
- package/src/errors.js +6 -0
- package/src/operations.js +6 -1
- package/src/schema.js +4 -4
- package/src/service.js +67 -26
- package/src/update.js +10 -0
- package/src/where.js +1 -1
package/index.d.ts
CHANGED
|
@@ -810,6 +810,7 @@ export interface SetRecordActionOptionsTransaction<A extends string, F extends s
|
|
|
810
810
|
append: SetRecordTransaction<A,F,C,S, AppendItem<A,F,C,S>,IndexCompositeAttributes,TableItem>;
|
|
811
811
|
delete: SetRecordTransaction<A,F,C,S, DeleteItem<A,F,C,S>,IndexCompositeAttributes,TableItem>;
|
|
812
812
|
data: DataUpdateMethodRecordTransaction<A,F,C,S, Item<A,F,C,S,S["attributes"]>,IndexCompositeAttributes,TableItem>;
|
|
813
|
+
composite: UpdateComposite<A,F,C,S, SetRecordActionOptionsTransaction<A,F,C,S,SetAttr,IndexCompositeAttributes,TableItem>>;
|
|
813
814
|
where: WhereClause<A,F,C,S, Item<A,F,C,S,S["attributes"]>,SetRecordActionOptionsTransaction<A,F,C,S,SetAttr,IndexCompositeAttributes,TableItem>>;
|
|
814
815
|
}
|
|
815
816
|
|
|
@@ -860,6 +861,7 @@ export interface SetRecordActionOptions<A extends string, F extends string, C ex
|
|
|
860
861
|
subtract: SetRecord<A,F,C,S, SubtractItem<A,F,C,S>,IndexCompositeAttributes,TableItem>;
|
|
861
862
|
append: SetRecord<A,F,C,S, AppendItem<A,F,C,S>,IndexCompositeAttributes,TableItem>;
|
|
862
863
|
delete: SetRecord<A,F,C,S, DeleteItem<A,F,C,S>,IndexCompositeAttributes,TableItem>;
|
|
864
|
+
composite: UpdateComposite<A,F,C,S, SetRecordActionOptions<A,F,C,S,SetAttr,IndexCompositeAttributes,TableItem>>;
|
|
863
865
|
data: DataUpdateMethodRecord<A,F,C,S, Item<A,F,C,S,S["attributes"]>,IndexCompositeAttributes,TableItem>;
|
|
864
866
|
where: WhereClause<A,F,C,S, Item<A,F,C,S,S["attributes"]>,SetRecordActionOptions<A,F,C,S,SetAttr,IndexCompositeAttributes,TableItem>>;
|
|
865
867
|
}
|
|
@@ -2171,6 +2173,12 @@ export type UpdatableItemAttribute<A extends Attribute> =
|
|
|
2171
2173
|
: never
|
|
2172
2174
|
: never
|
|
2173
2175
|
|
|
2176
|
+
type UpdateComposite<A extends string, F extends string, C extends string, S extends Schema<A,F,C>, Response> = (
|
|
2177
|
+
compositeAttributes: Partial<{
|
|
2178
|
+
[I in keyof S["indexes"] as I extends infer Name ? Name extends TableIndexName<A,F,C,S> ? never : Name : never]: IndexPKAttributes<A, F, C, S, I> & IndexSKAttributes<A, F, C, S, I>;
|
|
2179
|
+
}[keyof S["indexes"] extends infer KeyName ? KeyName extends TableIndexName<A,F,C,S> ? never : KeyName : never]>
|
|
2180
|
+
) => Response;
|
|
2181
|
+
|
|
2174
2182
|
export type RemovableItemAttribute<A extends Attribute> =
|
|
2175
2183
|
A['type'] extends OpaquePrimitiveTypeName<infer T>
|
|
2176
2184
|
? T
|
|
@@ -2541,6 +2549,7 @@ export class Entity<A extends string, F extends string, C extends string, S exte
|
|
|
2541
2549
|
append: SetRecord<A,F,C,S, AppendItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, Partial<ResponseItem<A,F,C,S>>>;
|
|
2542
2550
|
delete: SetRecord<A,F,C,S, DeleteItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, Partial<ResponseItem<A,F,C,S>>>;
|
|
2543
2551
|
data: DataUpdateMethodRecord<A,F,C,S, Item<A,F,C,S,S["attributes"]>, TableIndexCompositeAttributes<A,F,C,S>, Partial<ResponseItem<A,F,C,S>>>;
|
|
2552
|
+
composite: UpdateComposite<A,F,C,S, SetRecordActionOptions<A,F,C,S, SetItem<A,F,C,S>,TableIndexCompositeAttributes<A,F,C,S>, Partial<ResponseItem<A,F,C,S>>>>;
|
|
2544
2553
|
};
|
|
2545
2554
|
patch(key: AllTableIndexCompositeAttributes<A,F,C,S>): {
|
|
2546
2555
|
set: SetRecord<A,F,C,S, SetItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
@@ -2550,6 +2559,7 @@ export class Entity<A extends string, F extends string, C extends string, S exte
|
|
|
2550
2559
|
append: SetRecord<A,F,C,S, AppendItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
2551
2560
|
delete: SetRecord<A,F,C,S, DeleteItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
2552
2561
|
data: DataUpdateMethodRecord<A,F,C,S, Item<A,F,C,S,S["attributes"]>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
2562
|
+
composite: UpdateComposite<A,F,C,S, SetRecordActionOptions<A,F,C,S, SetItem<A,F,C,S>,TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>>;
|
|
2553
2563
|
};
|
|
2554
2564
|
|
|
2555
2565
|
find(record: Partial<Item<A,F,C,S,S["attributes"]>>): RecordsActionOptions<A,F,C,S, ResponseItem<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electrodb",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.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": {
|
|
@@ -31,8 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"homepage": "https://github.com/tywalch/electrodb#readme",
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@aws-sdk/client-dynamodb": "^3.
|
|
35
|
-
"@aws-sdk/lib-dynamodb": "^3.54.1",
|
|
34
|
+
"@aws-sdk/client-dynamodb": "^3.395.0",
|
|
36
35
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
|
37
36
|
"@types/chai": "^4.2.12",
|
|
38
37
|
"@types/mocha": "^8.0.3",
|
|
@@ -50,8 +49,8 @@
|
|
|
50
49
|
"nyc": "^15.1.0",
|
|
51
50
|
"source-map-support": "^0.5.19",
|
|
52
51
|
"ts-node": "^10.9.1",
|
|
53
|
-
"tsd": "^0.
|
|
54
|
-
"typescript": "^
|
|
52
|
+
"tsd": "^0.28.1",
|
|
53
|
+
"typescript": "^5.1.6",
|
|
55
54
|
"uuid": "7.0.1"
|
|
56
55
|
},
|
|
57
56
|
"keywords": [
|
|
@@ -67,7 +66,7 @@
|
|
|
67
66
|
"directory": "test"
|
|
68
67
|
},
|
|
69
68
|
"dependencies": {
|
|
70
|
-
"@aws-sdk/lib-dynamodb": "^3.
|
|
69
|
+
"@aws-sdk/lib-dynamodb": "^3.395.0",
|
|
71
70
|
"jsonschema": "1.2.7"
|
|
72
71
|
}
|
|
73
72
|
}
|
package/src/clauses.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { QueryTypes, MethodTypes, ItemOperations, ExpressionTypes, TransactionCommitSymbol, TransactionOperations, TerminalOperation, KeyTypes, IndexTypes } = require("./types");
|
|
2
|
-
const {AttributeOperationProxy, UpdateOperations, FilterOperationNames} = require("./operations");
|
|
2
|
+
const {AttributeOperationProxy, UpdateOperations, FilterOperationNames, UpdateOperationNames} = require("./operations");
|
|
3
3
|
const {UpdateExpression} = require("./update");
|
|
4
4
|
const {FilterExpression} = require("./where");
|
|
5
5
|
const v = require("./validations");
|
|
@@ -366,7 +366,7 @@ let clauses = {
|
|
|
366
366
|
return state;
|
|
367
367
|
}
|
|
368
368
|
},
|
|
369
|
-
children: ["set", "append","updateRemove", "updateDelete", "add", "subtract", "data", "commit"],
|
|
369
|
+
children: ["set", "append","updateRemove", "updateDelete", "add", "subtract", "data", "composite", "commit"],
|
|
370
370
|
},
|
|
371
371
|
update: {
|
|
372
372
|
name: "update",
|
|
@@ -393,7 +393,7 @@ let clauses = {
|
|
|
393
393
|
return state;
|
|
394
394
|
}
|
|
395
395
|
},
|
|
396
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
396
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
397
397
|
},
|
|
398
398
|
data: {
|
|
399
399
|
name: "data",
|
|
@@ -423,7 +423,7 @@ let clauses = {
|
|
|
423
423
|
return state;
|
|
424
424
|
}
|
|
425
425
|
},
|
|
426
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
426
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
427
427
|
},
|
|
428
428
|
set: {
|
|
429
429
|
name: "set",
|
|
@@ -440,7 +440,32 @@ let clauses = {
|
|
|
440
440
|
return state;
|
|
441
441
|
}
|
|
442
442
|
},
|
|
443
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
443
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
444
|
+
},
|
|
445
|
+
composite: {
|
|
446
|
+
name: "composite",
|
|
447
|
+
action(entity, state, composites = {}) {
|
|
448
|
+
if (state.getError() !== null) {
|
|
449
|
+
return state;
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
for (const attrName in composites) {
|
|
453
|
+
// todo: validate attrName is facet
|
|
454
|
+
if (entity.model.facets.byAttr[attrName]) {
|
|
455
|
+
const wasSet = state.query.update.addComposite(attrName, composites[attrName]);
|
|
456
|
+
if (!wasSet) {
|
|
457
|
+
throw new e.ElectroError(e.ErrorCodes.DuplicateUpdateCompositesProvided, `The composite attribute ${attrName} has been provided more than once with different values. Remove the duplication before running again`);
|
|
458
|
+
}
|
|
459
|
+
state.applyCondition(FilterOperationNames.eq, attrName, composites[attrName]);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return state;
|
|
463
|
+
} catch(err) {
|
|
464
|
+
state.setError(err);
|
|
465
|
+
return state;
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
444
469
|
},
|
|
445
470
|
append: {
|
|
446
471
|
name: "append",
|
|
@@ -457,7 +482,7 @@ let clauses = {
|
|
|
457
482
|
return state;
|
|
458
483
|
}
|
|
459
484
|
},
|
|
460
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
485
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
461
486
|
},
|
|
462
487
|
updateRemove: {
|
|
463
488
|
name: "remove",
|
|
@@ -477,7 +502,7 @@ let clauses = {
|
|
|
477
502
|
return state;
|
|
478
503
|
}
|
|
479
504
|
},
|
|
480
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
505
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
481
506
|
},
|
|
482
507
|
updateDelete: {
|
|
483
508
|
name: "delete",
|
|
@@ -494,7 +519,7 @@ let clauses = {
|
|
|
494
519
|
return state;
|
|
495
520
|
}
|
|
496
521
|
},
|
|
497
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
522
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
498
523
|
},
|
|
499
524
|
add: {
|
|
500
525
|
name: "add",
|
|
@@ -511,7 +536,7 @@ let clauses = {
|
|
|
511
536
|
return state;
|
|
512
537
|
}
|
|
513
538
|
},
|
|
514
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
539
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
515
540
|
},
|
|
516
541
|
subtract: {
|
|
517
542
|
name: "subtract",
|
|
@@ -528,7 +553,7 @@ let clauses = {
|
|
|
528
553
|
return state;
|
|
529
554
|
}
|
|
530
555
|
},
|
|
531
|
-
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit"],
|
|
556
|
+
children: ["data", "set", "append", "add", "updateRemove", "updateDelete", "go", "params", "subtract", "commit", "composite"],
|
|
532
557
|
},
|
|
533
558
|
query: {
|
|
534
559
|
name: "query",
|
|
@@ -965,6 +990,17 @@ class ChainState {
|
|
|
965
990
|
return this;
|
|
966
991
|
}
|
|
967
992
|
|
|
993
|
+
applyCondition(operation, name, ...values) {
|
|
994
|
+
if (FilterOperationNames[operation] !== undefined && name !== undefined && values.length > 0) {
|
|
995
|
+
const attribute = this.attributes[name];
|
|
996
|
+
if (attribute !== undefined) {
|
|
997
|
+
const filter = this.query.filter[ExpressionTypes.ConditionExpression];
|
|
998
|
+
filter.unsafeSet(operation, attribute.field, ...values);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return this;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
968
1004
|
unsafeApplyFilter(operation, name, ...values) {
|
|
969
1005
|
if (FilterOperationNames[operation] !== undefined & name !== undefined && values.length > 0) {
|
|
970
1006
|
const filter = this.query.filter[ExpressionTypes.FilterExpression];
|
package/src/entity.js
CHANGED
|
@@ -1884,7 +1884,11 @@ class Entity {
|
|
|
1884
1884
|
// We need to remove the pk/sk facets from before applying the Attribute setters because these values didnt
|
|
1885
1885
|
// change, and we also don't want to trigger the setters of any attributes watching these facets because that
|
|
1886
1886
|
// should only happen when an attribute is changed.
|
|
1887
|
-
const
|
|
1887
|
+
const attributesAndComposites = {
|
|
1888
|
+
...update.composites,
|
|
1889
|
+
...preparedUpdateValues,
|
|
1890
|
+
};
|
|
1891
|
+
const { indexKey, updatedKeys, deletedKeys = [] } = this._getUpdatedKeys(pk, sk, attributesAndComposites, removed);
|
|
1888
1892
|
const accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[TableIndex];
|
|
1889
1893
|
for (const path of Object.keys(preparedUpdateValues)) {
|
|
1890
1894
|
if (modifiedAttributeNames[path] !== undefined && preparedUpdateValues[path] !== undefined) {
|
|
@@ -2343,7 +2347,7 @@ class Entity {
|
|
|
2343
2347
|
let incompleteAccessPatterns = incomplete.map(({index}) => this.model.translations.indexes.fromIndexToAccessPattern[index]);
|
|
2344
2348
|
let missingFacets = incomplete.reduce((result, { missing }) => [...result, ...missing], []);
|
|
2345
2349
|
throw new e.ElectroError(e.ErrorCodes.IncompleteCompositeAttributes,
|
|
2346
|
-
`Incomplete composite attributes: Without the composite attributes ${u.commaSeparatedString(missingFacets)} the following access patterns cannot be updated: ${u.commaSeparatedString(incompleteAccessPatterns.filter((val) => val !== undefined))}
|
|
2350
|
+
`Incomplete composite attributes: Without the composite attributes ${u.commaSeparatedString(missingFacets)} the following access patterns cannot be updated: ${u.commaSeparatedString(incompleteAccessPatterns.filter((val) => val !== undefined))}. If a composite attribute is readOnly and cannot be set, use the 'composite' chain method on update to supply the value for key formatting purposes.`,
|
|
2347
2351
|
);
|
|
2348
2352
|
}
|
|
2349
2353
|
return complete;
|
package/src/errors.js
CHANGED
|
@@ -205,6 +205,12 @@ const ErrorCodes = {
|
|
|
205
205
|
name: "InvalidConversionCompositeProvided",
|
|
206
206
|
sym: ErrorCode,
|
|
207
207
|
},
|
|
208
|
+
DuplicateUpdateCompositesProvided: {
|
|
209
|
+
code: 2010,
|
|
210
|
+
section: "duplicate-update-composites-provided",
|
|
211
|
+
name: "DuplicateUpdateCompositesProvided",
|
|
212
|
+
sym: ErrorCode,
|
|
213
|
+
},
|
|
208
214
|
InvalidAttribute: {
|
|
209
215
|
code: 3001,
|
|
210
216
|
section: "invalid-attribute",
|
package/src/operations.js
CHANGED
|
@@ -552,4 +552,9 @@ const FilterOperationNames = Object.keys(FilterOperations).reduce((ops, name) =>
|
|
|
552
552
|
return ops;
|
|
553
553
|
}, {});
|
|
554
554
|
|
|
555
|
-
|
|
555
|
+
const UpdateOperationNames = Object.keys(UpdateOperations).reduce((ops, name) => {
|
|
556
|
+
ops[name] = name;
|
|
557
|
+
return ops;
|
|
558
|
+
}, {});
|
|
559
|
+
|
|
560
|
+
module.exports = {UpdateOperations, UpdateOperationNames, FilterOperations, FilterOperationNames, ExpressionState, AttributeOperationProxy};
|
package/src/schema.js
CHANGED
|
@@ -1171,7 +1171,7 @@ class Schema {
|
|
|
1171
1171
|
|
|
1172
1172
|
if (facets.byAttr && facets.byAttr[definition.name] !== undefined && (!ValidFacetTypes.includes(definition.type) && !Array.isArray(definition.type))) {
|
|
1173
1173
|
let assignedIndexes = facets.byAttr[name].map(assigned => assigned.index === "" ? "Table Index" : assigned.index);
|
|
1174
|
-
throw new e.ElectroError(e.ErrorCodes.InvalidAttributeDefinition, `Invalid composite attribute definition: Composite attributes must be one of the following: ${ValidFacetTypes.join(", ")}. The attribute "${name}" is defined as being type "${attribute.type}" but is a composite attribute of the
|
|
1174
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidAttributeDefinition, `Invalid composite attribute definition: Composite attributes must be one of the following: ${ValidFacetTypes.join(", ")}. The attribute "${name}" is defined as being type "${attribute.type}" but is a composite attribute of the following indexes: ${assignedIndexes.join(", ")}`);
|
|
1175
1175
|
}
|
|
1176
1176
|
|
|
1177
1177
|
if (usedAttrs[definition.field] || usedAttrs[name]) {
|
|
@@ -1438,9 +1438,9 @@ class Schema {
|
|
|
1438
1438
|
|
|
1439
1439
|
checkUpdate(payload = {}, { allowReadOnly } = {}) {
|
|
1440
1440
|
let record = {};
|
|
1441
|
-
for (let [path,
|
|
1442
|
-
let
|
|
1443
|
-
if (
|
|
1441
|
+
for (let [path, value] of Object.entries(payload)) {
|
|
1442
|
+
let attribute = this.traverser.paths.get(path);
|
|
1443
|
+
if (attribute === undefined) {
|
|
1444
1444
|
continue;
|
|
1445
1445
|
}
|
|
1446
1446
|
if (attribute.readOnly && !allowReadOnly) {
|
package/src/service.js
CHANGED
|
@@ -518,6 +518,8 @@ class Service {
|
|
|
518
518
|
}
|
|
519
519
|
|
|
520
520
|
_validateCollectionDefinition(definition = {}, providedIndex = {}) {
|
|
521
|
+
let isCustomMatchPK = definition.pk.isCustom === providedIndex.pk.isCustom;
|
|
522
|
+
let isCustomMatchSK = !!(definition.sk && definition.sk.isCustom) === !!(providedIndex.sk && providedIndex.sk.isCustom);
|
|
521
523
|
let indexMatch = definition.index === providedIndex.index;
|
|
522
524
|
let pkFieldMatch = definition.pk.field === providedIndex.pk.field;
|
|
523
525
|
let pkFacetLengthMatch = definition.pk.facets.length === providedIndex.pk.facets.length;
|
|
@@ -526,37 +528,74 @@ class Service {
|
|
|
526
528
|
let definitionIndexName = u.formatIndexNameForDisplay(definition.index);
|
|
527
529
|
let providedIndexName = u.formatIndexNameForDisplay(providedIndex.index);
|
|
528
530
|
let matchingKeyCasing = this._validateIndexCasingMatch(definition, providedIndex);
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
531
|
+
|
|
532
|
+
for (let i = 0; i < Math.max(definition.pk.labels.length, providedIndex.pk.labels.length); i++) {
|
|
533
|
+
let definitionFacet = definition.pk.labels[i] && definition.pk.labels[i].name;
|
|
534
|
+
let definitionLabel = definition.pk.labels[i] && definition.pk.labels[i].label;
|
|
535
|
+
let providedFacet = providedIndex.pk.labels[i] && providedIndex.pk.labels[i].name;
|
|
536
|
+
let providedLabel = providedIndex.pk.labels[i] && providedIndex.pk.labels[i].label;
|
|
537
|
+
let noLabels = definitionLabel === definitionFacet && providedLabel === providedFacet;
|
|
538
|
+
if (definitionLabel !== providedLabel) {
|
|
539
|
+
mismatchedFacetLabels.push({
|
|
540
|
+
definitionFacet,
|
|
541
|
+
definitionLabel,
|
|
542
|
+
providedFacet,
|
|
543
|
+
providedLabel,
|
|
544
|
+
kind: "Partition",
|
|
545
|
+
type: noLabels ? "facet" : "label"
|
|
546
|
+
});
|
|
547
|
+
break;
|
|
548
|
+
} else if (definitionFacet !== providedFacet) {
|
|
549
|
+
mismatchedFacetLabels.push({
|
|
550
|
+
definitionFacet,
|
|
551
|
+
definitionLabel,
|
|
552
|
+
providedFacet,
|
|
553
|
+
providedLabel,
|
|
554
|
+
kind: "Partition",
|
|
555
|
+
type: "facet"
|
|
556
|
+
});
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (!isCustomMatchPK) {
|
|
562
|
+
collectionDifferences.push(`The usage of key templates the partition key on index ${definitionIndexName} must be consistent across all Entities, some entities provided use template while others do not`);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (!isCustomMatchSK) {
|
|
566
|
+
collectionDifferences.push(`The usage of key templates the sort key on index ${definitionIndexName} must be consistent across all Entities, some entities provided use template while others do not`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (definition.type === "clustered") {
|
|
570
|
+
for (let i = 0; i < Math.min(definition.sk.labels.length, providedIndex.sk.labels.length); i++) {
|
|
571
|
+
let definitionFacet = definition.sk.labels[i] && definition.sk.labels[i].name;
|
|
572
|
+
let definitionLabel = definition.sk.labels[i] && definition.sk.labels[i].label;
|
|
573
|
+
let providedFacet = providedIndex.sk.labels[i] && providedIndex.sk.labels[i].name;
|
|
574
|
+
let providedLabel = providedIndex.sk.labels[i] && providedIndex.sk.labels[i].label;
|
|
575
|
+
let noLabels = definitionLabel === definitionFacet && providedLabel === providedFacet;
|
|
576
|
+
if (definitionFacet === providedFacet) {
|
|
577
|
+
if (definitionLabel !== providedLabel) {
|
|
578
|
+
mismatchedFacetLabels.push({
|
|
579
|
+
definitionFacet,
|
|
580
|
+
definitionLabel,
|
|
581
|
+
providedFacet,
|
|
582
|
+
providedLabel,
|
|
583
|
+
kind: "Sort",
|
|
584
|
+
type: noLabels ? "facet" : "label"
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
break;
|
|
552
589
|
}
|
|
553
590
|
}
|
|
554
591
|
}
|
|
592
|
+
|
|
555
593
|
if (!matchingKeyCasing.pk) {
|
|
556
594
|
collectionDifferences.push(
|
|
557
595
|
`The pk property "casing" provided "${providedIndex.pk.casing || KeyCasing.default}" does not match established casing "${definition.pk.casing || KeyCasing.default}" on index "${providedIndexName}". Index casing options must match across all entities participating in a collection`
|
|
558
596
|
);
|
|
559
597
|
}
|
|
598
|
+
|
|
560
599
|
if (!matchingKeyCasing.sk) {
|
|
561
600
|
const definedSk = definition.sk || {};
|
|
562
601
|
const providedSk = providedIndex.sk || {};
|
|
@@ -564,6 +603,7 @@ class Service {
|
|
|
564
603
|
`The sk property "casing" provided "${definedSk.casing || KeyCasing.default}" does not match established casing "${providedSk.casing || KeyCasing.default}" on index "${providedIndexName}". Index casing options must match across all entities participating in a collection`
|
|
565
604
|
);
|
|
566
605
|
}
|
|
606
|
+
|
|
567
607
|
if (!indexMatch) {
|
|
568
608
|
collectionDifferences.push(
|
|
569
609
|
`Collection defined on provided index "${providedIndexName}" does not match collection established index "${definitionIndexName}". Collections must be defined on the same index across all entities within a service.`,
|
|
@@ -573,6 +613,7 @@ class Service {
|
|
|
573
613
|
`Partition Key composite attributes provided "${providedIndex.pk.field}" for index "${providedIndexName}" do not match established field "${definition.pk.field}" on established index "${definitionIndexName}"`,
|
|
574
614
|
);
|
|
575
615
|
}
|
|
616
|
+
|
|
576
617
|
if (!pkFacetLengthMatch) {
|
|
577
618
|
collectionDifferences.push(
|
|
578
619
|
`Partition Key composite attributes provided [${providedIndex.pk.facets.map(val => `"${val}"`).join(", ")}] for index "${providedIndexName}" do not match established composite attributes [${definition.pk.facets.map(val => `"${val}"`).join(", ")}] on established index "${definitionIndexName}"`,
|
|
@@ -583,11 +624,11 @@ class Service {
|
|
|
583
624
|
for (let mismatch of mismatchedFacetLabels) {
|
|
584
625
|
if (mismatch.type === "facet") {
|
|
585
626
|
collectionDifferences.push(
|
|
586
|
-
|
|
627
|
+
`${mismatch.kind} Key composite attributes provided for index "${providedIndexName}" do not match established composite attribute "${mismatch.definitionFacet}" on established index "${definitionIndexName}": "${mismatch.definitionLabel}" != "${mismatch.providedLabel}"; Composite attribute definitions must match between all members of a collection to ensure key structures will resolve to identical Partition Keys. Please ensure these composite attribute definitions are identical for all entities associated with this service.`
|
|
587
628
|
);
|
|
588
629
|
} else {
|
|
589
630
|
collectionDifferences.push(
|
|
590
|
-
|
|
631
|
+
`${mismatch.kind} Key composite attributes provided for index "${providedIndexName}" contain conflicting composite attribute labels for established composite attribute "${mismatch.definitionFacet || ""}" on established index "${definitionIndexName}". Established composite attribute "${mismatch.definitionFacet || ""}" on established index "${definitionIndexName}" was defined with label "${mismatch.definitionLabel}" while provided composite attribute "${mismatch.providedFacet || ""}" on provided index "${providedIndexName}" is defined with label "${mismatch.providedLabel}". Composite attribute labels definitions must match between all members of a collection to ensure key structures will resolve to identical Partition Keys. Please ensure these labels definitions are identical for all entities associated with this service.`
|
|
591
632
|
);
|
|
592
633
|
}
|
|
593
634
|
|
|
@@ -644,7 +685,7 @@ class Service {
|
|
|
644
685
|
}
|
|
645
686
|
const [invalidDefinition, invalidIndexMessages] = this._validateCollectionDefinition(definition, providedIndex);
|
|
646
687
|
if (invalidDefinition) {
|
|
647
|
-
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Validation Error while joining entity, "${name}". ${invalidIndexMessages.join("
|
|
688
|
+
throw new e.ElectroError(e.ErrorCodes.InvalidJoin, `Validation Error while joining entity, "${name}". ${invalidIndexMessages.join("; ")}`);
|
|
648
689
|
}
|
|
649
690
|
const sharedSortKeyAttributes = [];
|
|
650
691
|
const sharedSortKeyCompositeAttributeLabels = [];
|
package/src/update.js
CHANGED
|
@@ -11,9 +11,19 @@ class UpdateExpression extends ExpressionState {
|
|
|
11
11
|
subtract: new Set(),
|
|
12
12
|
delete: new Set(),
|
|
13
13
|
};
|
|
14
|
+
this.composites = {};
|
|
14
15
|
this.seen = new Map();
|
|
15
16
|
this.type = BuilderTypes.update;
|
|
16
17
|
}
|
|
18
|
+
addComposite(attrName, value) {
|
|
19
|
+
if (value !== undefined) {
|
|
20
|
+
if (this.composites[attrName] === undefined || this.composites[attrName] === value) {
|
|
21
|
+
this.composites[attrName] = value;
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
17
27
|
|
|
18
28
|
add(type, expression) {
|
|
19
29
|
this.operations[type].add(expression);
|
package/src/where.js
CHANGED
|
@@ -47,7 +47,7 @@ class FilterExpression extends ExpressionState {
|
|
|
47
47
|
unsafeSet(operation, name, ...values) {
|
|
48
48
|
const {template} = FilterOperations[operation] || {};
|
|
49
49
|
if (template === undefined) {
|
|
50
|
-
throw new Error(`Invalid operation: "${operation}". Please report
|
|
50
|
+
throw new Error(`Invalid operation: "${operation}". Please report this issue via a bug ticket.`);
|
|
51
51
|
}
|
|
52
52
|
const names = this.setName({}, name, name);
|
|
53
53
|
const valueExpressions = values.map(value => this.setValue(name, value));
|