electrodb 1.8.3 → 1.10.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/README.md +23 -19
- package/index.d.ts +3 -1551
- package/package.json +7 -7
- package/src/entity.d.ts +94 -0
- package/src/entity.js +91 -34
- package/src/entity.test-d.ts +110 -0
- package/src/errors.js +6 -0
- package/src/service.d.ts +17 -0
- package/src/types/client.ts +15 -0
- package/src/types/collections.ts +243 -0
- package/src/types/events.ts +47 -0
- package/src/types/index.ts +67 -0
- package/src/types/model.ts +132 -0
- package/src/types/options.ts +81 -0
- package/src/types/schema.test-d.ts +2507 -0
- package/src/types/schema.ts +1016 -0
- package/src/types/tests.test-d.ts +3 -0
- package/src/types/types.test-d.ts +142 -0
- package/src/types/where.test-d.ts +1939 -0
- package/src/types/where.ts +94 -0
- package/src/util.js +36 -3
- package/CHANGELOG.md +0 -181
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "electrodb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"test-ts": "mocha -r ts-node/register ./test/**.spec.ts",
|
|
9
9
|
"test-all": "mocha ./test/**.spec.js",
|
|
10
10
|
"test-all-local": "LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 node ./test/init.js && LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 mocha ./test/**.spec.js && npm run test-types && LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 npm run test-ts",
|
|
11
|
-
"test-types": "
|
|
11
|
+
"test-types": "tsd",
|
|
12
12
|
"coverage": "nyc npm run test-all && nyc report --reporter=text-lcov | coveralls",
|
|
13
13
|
"coverage-coveralls-local": "nyc npm run test-all-local && nyc report --reporter=text-lcov | coveralls",
|
|
14
14
|
"coverage-html-local": "nyc npm run test-all-local && nyc report --reporter=html",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"moment": "2.24.0",
|
|
44
44
|
"nyc": "^15.1.0",
|
|
45
45
|
"source-map-support": "^0.5.19",
|
|
46
|
-
"ts-node": "^
|
|
47
|
-
"tsd": "^0.
|
|
48
|
-
"typescript": "^4.
|
|
46
|
+
"ts-node": "^10.8.1",
|
|
47
|
+
"tsd": "^0.21.0",
|
|
48
|
+
"typescript": "^4.7.4",
|
|
49
49
|
"uuid": "7.0.1"
|
|
50
50
|
},
|
|
51
51
|
"keywords": [
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"directory": "test"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"
|
|
65
|
-
"
|
|
64
|
+
"@aws-sdk/lib-dynamodb": "^3.54.1",
|
|
65
|
+
"jsonschema": "1.2.7"
|
|
66
66
|
}
|
|
67
67
|
}
|
package/src/entity.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ParseOptions
|
|
3
|
+
} from './types/options';
|
|
4
|
+
import {
|
|
5
|
+
TableIndexCompositeAttributes,
|
|
6
|
+
AllTableIndexCompositeAttributes,
|
|
7
|
+
ResponseItem,
|
|
8
|
+
Item,
|
|
9
|
+
Schema,
|
|
10
|
+
SetItem,
|
|
11
|
+
AddItem,
|
|
12
|
+
SubtractItem,
|
|
13
|
+
AppendItem,
|
|
14
|
+
DeleteItem,
|
|
15
|
+
RemoveItem,
|
|
16
|
+
PutItem,
|
|
17
|
+
} from './types/schema';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
Queries,
|
|
21
|
+
SetRecord,
|
|
22
|
+
RemoveRecord,
|
|
23
|
+
DataUpdateMethodRecord,
|
|
24
|
+
RecordsActionOptions,
|
|
25
|
+
SingleRecordOperationOptions,
|
|
26
|
+
BulkRecordOperationOptions,
|
|
27
|
+
DeleteRecordOperationOptions,
|
|
28
|
+
PutRecordOperationOptions,
|
|
29
|
+
ParseSingleInput,
|
|
30
|
+
ParseMultiInput
|
|
31
|
+
} from './types/model';
|
|
32
|
+
|
|
33
|
+
import { ElectroEventListener } from './types/events';
|
|
34
|
+
import { DocumentClient } from './types/client';
|
|
35
|
+
|
|
36
|
+
export type Resolve<T> = T extends Function | string | number | boolean
|
|
37
|
+
? T : {[Key in keyof T]: Resolve<T[Key]>}
|
|
38
|
+
|
|
39
|
+
export type EntityConfiguration = {
|
|
40
|
+
table?: string;
|
|
41
|
+
client?: DocumentClient;
|
|
42
|
+
listeners?: Array<ElectroEventListener>;
|
|
43
|
+
logger?: ElectroEventListener;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export class Entity<A extends string, F extends string, C extends string, S extends Schema<A,F,C>> {
|
|
47
|
+
readonly schema: S;
|
|
48
|
+
private config?: EntityConfiguration;
|
|
49
|
+
constructor(schema: S, config?: EntityConfiguration);
|
|
50
|
+
|
|
51
|
+
get(key: AllTableIndexCompositeAttributes<A,F,C,S>): SingleRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S> | null>;
|
|
52
|
+
get(key: AllTableIndexCompositeAttributes<A,F,C,S>[]): BulkRecordOperationOptions<A,F,C,S, [Array<Resolve<ResponseItem<A,F,C,S>>>, Array<Resolve<AllTableIndexCompositeAttributes<A,F,C,S>>>], [Array<Resolve<ResponseItem<A,F,C,S>> | null>, Array<Resolve<AllTableIndexCompositeAttributes<A,F,C,S>>>]>;
|
|
53
|
+
|
|
54
|
+
delete(key: AllTableIndexCompositeAttributes<A,F,C,S>): DeleteRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>;
|
|
55
|
+
delete(key: AllTableIndexCompositeAttributes<A,F,C,S>[]): BulkRecordOperationOptions<A,F,C,S, AllTableIndexCompositeAttributes<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>[]>;
|
|
56
|
+
|
|
57
|
+
put(record: PutItem<A,F,C,S>): PutRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>;
|
|
58
|
+
put(record: PutItem<A,F,C,S>[]): BulkRecordOperationOptions<A,F,C,S, AllTableIndexCompositeAttributes<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>[]>;
|
|
59
|
+
|
|
60
|
+
remove(key: AllTableIndexCompositeAttributes<A,F,C,S>): DeleteRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>
|
|
61
|
+
update(key: AllTableIndexCompositeAttributes<A,F,C,S>): {
|
|
62
|
+
set: SetRecord<A,F,C,S, SetItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
63
|
+
remove: RemoveRecord<A,F,C,S, RemoveItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
64
|
+
add: SetRecord<A,F,C,S, AddItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
65
|
+
subtract: SetRecord<A,F,C,S, SubtractItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
66
|
+
append: SetRecord<A,F,C,S, AppendItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
67
|
+
delete: SetRecord<A,F,C,S, DeleteItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
68
|
+
data: DataUpdateMethodRecord<A,F,C,S, Item<A,F,C,S,S["attributes"]>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
patch(key: AllTableIndexCompositeAttributes<A,F,C,S>): {
|
|
72
|
+
set: SetRecord<A,F,C,S, SetItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
73
|
+
remove: RemoveRecord<A,F,C,S, RemoveItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
74
|
+
add: SetRecord<A,F,C,S, AddItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
75
|
+
subtract: SetRecord<A,F,C,S, SubtractItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
76
|
+
append: SetRecord<A,F,C,S, AppendItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
77
|
+
delete: SetRecord<A,F,C,S, DeleteItem<A,F,C,S>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
78
|
+
data: DataUpdateMethodRecord<A,F,C,S, Item<A,F,C,S,S["attributes"]>, TableIndexCompositeAttributes<A,F,C,S>, ResponseItem<A,F,C,S>>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
create(record: PutItem<A,F,C,S>): PutRecordOperationOptions<A,F,C,S, ResponseItem<A,F,C,S>>
|
|
82
|
+
|
|
83
|
+
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>>;
|
|
84
|
+
|
|
85
|
+
match(record: Partial<Item<A,F,C,S,S["attributes"]>>): RecordsActionOptions<A,F,C,S, ResponseItem<A,F,C,S>[], AllTableIndexCompositeAttributes<A,F,C,S>>;
|
|
86
|
+
|
|
87
|
+
scan: RecordsActionOptions<A,F,C,S, ResponseItem<A,F,C,S>[], TableIndexCompositeAttributes<A,F,C,S>>;
|
|
88
|
+
query: Queries<A,F,C,S>;
|
|
89
|
+
|
|
90
|
+
parse(item: ParseSingleInput, options?: ParseOptions): ResponseItem<A,F,C,S> | null;
|
|
91
|
+
parse(item: ParseMultiInput, options?: ParseOptions): ResponseItem<A,F,C,S>[];
|
|
92
|
+
setIdentifier(type: "entity" | "version", value: string): void;
|
|
93
|
+
client: any;
|
|
94
|
+
}
|
package/src/entity.js
CHANGED
|
@@ -277,6 +277,7 @@ class Entity {
|
|
|
277
277
|
results,
|
|
278
278
|
}, config.listeners);
|
|
279
279
|
}
|
|
280
|
+
|
|
280
281
|
return this.client[method](params).promise()
|
|
281
282
|
.then((results) => {
|
|
282
283
|
notifyQuery();
|
|
@@ -317,14 +318,36 @@ class Entity {
|
|
|
317
318
|
return results;
|
|
318
319
|
}
|
|
319
320
|
|
|
321
|
+
_createNewBatchGetOrderMaintainer(config = {}) {
|
|
322
|
+
const pkName = this.model.translations.keys[TableIndex].pk;
|
|
323
|
+
const skName = this.model.translations.keys[TableIndex].sk;
|
|
324
|
+
const enabled = !!config.preserveBatchOrder;
|
|
325
|
+
const table = this.config.table;
|
|
326
|
+
const keyFormatter = ((record = {}) => {
|
|
327
|
+
const pk = record[pkName];
|
|
328
|
+
const sk = record[skName];
|
|
329
|
+
return `${pk}${sk}`;
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
return new u.BatchGetOrderMaintainer({
|
|
333
|
+
table,
|
|
334
|
+
enabled,
|
|
335
|
+
keyFormatter,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
320
339
|
async executeBulkGet(parameters, config) {
|
|
321
340
|
if (!Array.isArray(parameters)) {
|
|
322
341
|
parameters = [parameters];
|
|
323
342
|
}
|
|
324
|
-
let concurrent = this._normalizeConcurrencyValue(config.concurrent)
|
|
325
|
-
let concurrentOperations = u.batchItems(parameters, concurrent);
|
|
326
343
|
|
|
327
|
-
|
|
344
|
+
const orderMaintainer = this._createNewBatchGetOrderMaintainer(config);
|
|
345
|
+
orderMaintainer.defineOrder(parameters);
|
|
346
|
+
let concurrent = this._normalizeConcurrencyValue(config.concurrent);
|
|
347
|
+
let concurrentOperations = u.batchItems(parameters, concurrent);
|
|
348
|
+
let resultsAll = config.preserveBatchOrder
|
|
349
|
+
? new Array(orderMaintainer.getSize()).fill(null)
|
|
350
|
+
: [];
|
|
328
351
|
let unprocessedAll = [];
|
|
329
352
|
for (let operation of concurrentOperations) {
|
|
330
353
|
await Promise.all(operation.map(async params => {
|
|
@@ -333,13 +356,13 @@ class Entity {
|
|
|
333
356
|
resultsAll.push(await config.parse(config, response));
|
|
334
357
|
return;
|
|
335
358
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
resultsAll
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
359
|
+
this.applyBulkGetResponseFormatting({
|
|
360
|
+
orderMaintainer,
|
|
361
|
+
resultsAll,
|
|
362
|
+
unprocessedAll,
|
|
363
|
+
response,
|
|
364
|
+
config
|
|
365
|
+
});
|
|
343
366
|
}));
|
|
344
367
|
}
|
|
345
368
|
return [resultsAll, unprocessedAll];
|
|
@@ -467,20 +490,26 @@ class Entity {
|
|
|
467
490
|
}
|
|
468
491
|
}
|
|
469
492
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
493
|
+
applyBulkGetResponseFormatting({
|
|
494
|
+
resultsAll,
|
|
495
|
+
unprocessedAll,
|
|
496
|
+
orderMaintainer,
|
|
497
|
+
response = {},
|
|
498
|
+
config = {},
|
|
499
|
+
}) {
|
|
473
500
|
const table = config.table || this._getTableName();
|
|
474
501
|
const index = TableIndex;
|
|
502
|
+
|
|
475
503
|
if (!response.UnprocessedKeys || !response.Responses) {
|
|
476
504
|
throw new Error("Unknown response format");
|
|
477
505
|
}
|
|
506
|
+
|
|
478
507
|
if (response.UnprocessedKeys[table] && response.UnprocessedKeys[table].Keys && Array.isArray(response.UnprocessedKeys[table].Keys)) {
|
|
479
508
|
for (let value of response.UnprocessedKeys[table].Keys) {
|
|
480
509
|
if (config && config.unprocessed === UnprocessedTypes.raw) {
|
|
481
|
-
|
|
510
|
+
unprocessedAll.push(value);
|
|
482
511
|
} else {
|
|
483
|
-
|
|
512
|
+
unprocessedAll.push(
|
|
484
513
|
this._formatKeysToItem(index, value)
|
|
485
514
|
);
|
|
486
515
|
}
|
|
@@ -488,10 +517,18 @@ class Entity {
|
|
|
488
517
|
}
|
|
489
518
|
|
|
490
519
|
if (response.Responses[table] && Array.isArray(response.Responses[table])) {
|
|
491
|
-
|
|
520
|
+
const responses = response.Responses[table];
|
|
521
|
+
for (let i = 0; i < responses.length; i++) {
|
|
522
|
+
const item = responses[i];
|
|
523
|
+
const slot = orderMaintainer.getOrder(item);
|
|
524
|
+
const formatted = this.formatResponse({Item: item}, index, config);
|
|
525
|
+
if (slot !== -1) {
|
|
526
|
+
resultsAll[slot] = formatted;
|
|
527
|
+
} else {
|
|
528
|
+
resultsAll.push(formatted);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
492
531
|
}
|
|
493
|
-
|
|
494
|
-
return [results, unprocessed];
|
|
495
532
|
}
|
|
496
533
|
|
|
497
534
|
formatResponse(response, index, config = {}) {
|
|
@@ -782,6 +819,7 @@ class Entity {
|
|
|
782
819
|
_isCollectionQuery: false,
|
|
783
820
|
pages: undefined,
|
|
784
821
|
listeners: [],
|
|
822
|
+
preserveBatchOrder: false,
|
|
785
823
|
};
|
|
786
824
|
|
|
787
825
|
config = options.reduce((config, option) => {
|
|
@@ -794,6 +832,10 @@ class Entity {
|
|
|
794
832
|
config.params.ReturnValues = FormatToReturnValues[format];
|
|
795
833
|
}
|
|
796
834
|
|
|
835
|
+
if (option.preserveBatchOrder === true) {
|
|
836
|
+
config.preserveBatchOrder = true;
|
|
837
|
+
}
|
|
838
|
+
|
|
797
839
|
if (option.pages !== undefined) {
|
|
798
840
|
config.pages = option.pages;
|
|
799
841
|
}
|
|
@@ -2257,22 +2299,6 @@ class Entity {
|
|
|
2257
2299
|
facets.fields.push(sk.field);
|
|
2258
2300
|
}
|
|
2259
2301
|
|
|
2260
|
-
if (seenIndexFields[pk.field] !== undefined) {
|
|
2261
|
-
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Partition Key (pk) on Access Pattern '${accessPattern}' references the field '${pk.field}' which is already referenced by the Access Pattern '${seenIndexFields[pk.field]}'. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2262
|
-
} else {
|
|
2263
|
-
seenIndexFields[pk.field] = accessPattern;
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
if (sk.field) {
|
|
2267
|
-
if (sk.field === pk.field) {
|
|
2268
|
-
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `The Access Pattern '${accessPattern}' references the field '${sk.field}' as the field name for both the PK and SK. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2269
|
-
} else if (seenIndexFields[sk.field] !== undefined) {
|
|
2270
|
-
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Sort Key (sk) on Access Pattern '${accessPattern}' references the field '${sk.field}' which is already referenced by the Access Pattern '${seenIndexFields[sk.field]}'. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2271
|
-
}else {
|
|
2272
|
-
seenIndexFields[sk.field] = accessPattern;
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
2302
|
if (Array.isArray(sk.facets)) {
|
|
2277
2303
|
let duplicates = pk.facets.filter(facet => sk.facets.includes(facet));
|
|
2278
2304
|
if (duplicates.length !== 0) {
|
|
@@ -2362,6 +2388,37 @@ class Entity {
|
|
|
2362
2388
|
facets.byField[sk.field][indexName] = sk;
|
|
2363
2389
|
}
|
|
2364
2390
|
|
|
2391
|
+
if (seenIndexFields[pk.field] !== undefined) {
|
|
2392
|
+
const definition = Object.values(facets.byField[pk.field]).find(definition => definition.index !== indexName)
|
|
2393
|
+
const definitionsMatch = validations.stringArrayMatch(pk.facets, definition.facets);
|
|
2394
|
+
if (!definitionsMatch) {
|
|
2395
|
+
throw new e.ElectroError(e.ErrorCodes.InconsistentIndexDefinition, `Partition Key (pk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' is defined with the composite attribute(s) ${u.commaSeparatedString(pk.facets)}, but the accessPattern '${u.formatIndexNameForDisplay(definition.index)}' defines this field with the composite attributes ${u.commaSeparatedString(definition.facets)}'. Key fields must have the same composite attribute definitions across all indexes they are involved with`);
|
|
2396
|
+
}
|
|
2397
|
+
seenIndexFields[pk.field].push({accessPattern, type: 'pk'});
|
|
2398
|
+
} else {
|
|
2399
|
+
seenIndexFields[pk.field] = [];
|
|
2400
|
+
seenIndexFields[pk.field].push({accessPattern, type: 'pk'});
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
if (sk.field) {
|
|
2404
|
+
if (sk.field === pk.field) {
|
|
2405
|
+
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `The Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' references the field '${sk.field}' as the field name for both the PK and SK. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2406
|
+
} else if (seenIndexFields[sk.field] !== undefined) {
|
|
2407
|
+
const isAlsoDefinedAsPK = seenIndexFields[sk.field].find(field => field.type === "pk");
|
|
2408
|
+
if (isAlsoDefinedAsPK) {
|
|
2409
|
+
throw new e.ElectroError(e.ErrorCodes.InconsistentIndexDefinition, `The Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' references the field '${pk.field}' which is already referenced by the Access Pattern(s) '${u.formatIndexNameForDisplay(isAlsoDefinedAsPK.accessPattern)}' as a Partition Key. Fields mapped to Partition Keys cannot be also mapped to Sort Keys.`);
|
|
2410
|
+
}
|
|
2411
|
+
const definition = Object.values(facets.byField[sk.field]).find(definition => definition.index !== indexName)
|
|
2412
|
+
const definitionsMatch = validations.stringArrayMatch(sk.facets, definition.facets);
|
|
2413
|
+
if (!definitionsMatch) {
|
|
2414
|
+
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' is defined with the composite attribute(s) ${u.commaSeparatedString(sk.facets)}, but the accessPattern '${u.formatIndexNameForDisplay(definition.index)}' defines this field with the composite attributes ${u.commaSeparatedString(definition.facets)}'. Key fields must have the same composite attribute definitions across all indexes they are involved with`);
|
|
2415
|
+
}
|
|
2416
|
+
seenIndexFields[sk.field].push({accessPattern, type: 'sk'});
|
|
2417
|
+
} else {
|
|
2418
|
+
seenIndexFields[sk.field] = [];
|
|
2419
|
+
seenIndexFields[sk.field].push({accessPattern, type: 'sk'});
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2365
2422
|
|
|
2366
2423
|
attributes.forEach(({index, type, name}, j) => {
|
|
2367
2424
|
let next = attributes[j + 1] !== undefined ? attributes[j + 1].name : "";
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Entity } from './entity';
|
|
2
|
+
|
|
3
|
+
const entityWithSK = new Entity({
|
|
4
|
+
model: {
|
|
5
|
+
entity: "abc",
|
|
6
|
+
service: "myservice",
|
|
7
|
+
version: "myversion"
|
|
8
|
+
},
|
|
9
|
+
attributes: {
|
|
10
|
+
attr1: {
|
|
11
|
+
type: "string",
|
|
12
|
+
default: "abc",
|
|
13
|
+
get: (val) => val + 123,
|
|
14
|
+
set: (val) => (val ?? "") + 456,
|
|
15
|
+
validate: (val) => !!val,
|
|
16
|
+
},
|
|
17
|
+
attr2: {
|
|
18
|
+
type: "string",
|
|
19
|
+
// default: () => "sfg",
|
|
20
|
+
// required: false,
|
|
21
|
+
validate: (val) => val.length > 0
|
|
22
|
+
},
|
|
23
|
+
attr3: {
|
|
24
|
+
type: ["123", "def", "ghi"] as const,
|
|
25
|
+
default: "def"
|
|
26
|
+
},
|
|
27
|
+
attr4: {
|
|
28
|
+
type: ["abc", "ghi"] as const,
|
|
29
|
+
required: true
|
|
30
|
+
},
|
|
31
|
+
attr5: {
|
|
32
|
+
type: "string"
|
|
33
|
+
},
|
|
34
|
+
attr6: {
|
|
35
|
+
type: "number",
|
|
36
|
+
default: () => 100,
|
|
37
|
+
get: (val) => val + 5,
|
|
38
|
+
set: (val) => (val ?? 0) + 5,
|
|
39
|
+
validate: (val) => true,
|
|
40
|
+
},
|
|
41
|
+
attr7: {
|
|
42
|
+
type: "any",
|
|
43
|
+
default: () => false,
|
|
44
|
+
get: (val) => ({key: "value"}),
|
|
45
|
+
set: (val) => (val ?? 0) + 5,
|
|
46
|
+
validate: (val) => true,
|
|
47
|
+
},
|
|
48
|
+
attr8: {
|
|
49
|
+
type: "boolean",
|
|
50
|
+
required: true,
|
|
51
|
+
get: (val) => !!val,
|
|
52
|
+
set: (val) => !!val,
|
|
53
|
+
validate: (val) => !!val,
|
|
54
|
+
},
|
|
55
|
+
attr9: {
|
|
56
|
+
type: "number"
|
|
57
|
+
},
|
|
58
|
+
attr10: {
|
|
59
|
+
type: "boolean"
|
|
60
|
+
},
|
|
61
|
+
attr11: {
|
|
62
|
+
type: 'list',
|
|
63
|
+
items: {
|
|
64
|
+
type: 'string'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
indexes: {
|
|
69
|
+
myIndex: {
|
|
70
|
+
collection: "mycollection2",
|
|
71
|
+
pk: {
|
|
72
|
+
field: "pk",
|
|
73
|
+
composite: ["attr1"]
|
|
74
|
+
},
|
|
75
|
+
sk: {
|
|
76
|
+
field: "sk",
|
|
77
|
+
composite: ["attr2"]
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
myIndex2: {
|
|
81
|
+
collection: "mycollection1",
|
|
82
|
+
index: "gsi1",
|
|
83
|
+
pk: {
|
|
84
|
+
field: "gsipk1",
|
|
85
|
+
composite: ["attr6", "attr9"]
|
|
86
|
+
},
|
|
87
|
+
sk: {
|
|
88
|
+
field: "gsisk1",
|
|
89
|
+
composite: ["attr4", "attr5"]
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
myIndex3: {
|
|
93
|
+
collection: "mycollection",
|
|
94
|
+
index: "gsi2",
|
|
95
|
+
pk: {
|
|
96
|
+
field: "gsipk2",
|
|
97
|
+
composite: ["attr5"]
|
|
98
|
+
},
|
|
99
|
+
sk: {
|
|
100
|
+
field: "gsisk2",
|
|
101
|
+
composite: ["attr4", "attr3", "attr9"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
entityWithSK.update({
|
|
108
|
+
attr1: 'abc',
|
|
109
|
+
attr2: 'def'
|
|
110
|
+
}).append({})
|
package/src/errors.js
CHANGED
|
@@ -139,6 +139,12 @@ const ErrorCodes = {
|
|
|
139
139
|
name: "InvalidClientProvided",
|
|
140
140
|
sym: ErrorCode,
|
|
141
141
|
},
|
|
142
|
+
InconsistentIndexDefinition: {
|
|
143
|
+
code: 1022,
|
|
144
|
+
section: "inconsistent-index-definition",
|
|
145
|
+
name: "InvalidClientProvided",
|
|
146
|
+
sym: ErrorCode,
|
|
147
|
+
},
|
|
142
148
|
MissingAttribute: {
|
|
143
149
|
code: 2001,
|
|
144
150
|
section: "missing-attribute",
|
package/src/service.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Entity } from './entity';
|
|
2
|
+
import { DocumentClient } from './types/client';
|
|
3
|
+
import { ElectroEventListener } from './types/events';
|
|
4
|
+
import { CollectionQueries, CollectionAssociations } from './types/collections';
|
|
5
|
+
|
|
6
|
+
export type ServiceConfiguration = {
|
|
7
|
+
table?: string;
|
|
8
|
+
client?: DocumentClient;
|
|
9
|
+
listeners?: Array<ElectroEventListener>;
|
|
10
|
+
logger?: ElectroEventListener;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export class Service<E extends {[name: string]: Entity<any, any, any, any>}> {
|
|
14
|
+
entities: E;
|
|
15
|
+
collections: CollectionQueries<E, CollectionAssociations<E>>
|
|
16
|
+
constructor(entities: E, config?: ServiceConfiguration);
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
type DocumentClientMethod = (parameters: any) => {promise: () => Promise<any>};
|
|
2
|
+
|
|
3
|
+
export type DocumentClient = {
|
|
4
|
+
get: DocumentClientMethod;
|
|
5
|
+
put: DocumentClientMethod;
|
|
6
|
+
delete: DocumentClientMethod;
|
|
7
|
+
update: DocumentClientMethod;
|
|
8
|
+
batchWrite: DocumentClientMethod;
|
|
9
|
+
batchGet: DocumentClientMethod;
|
|
10
|
+
scan: DocumentClientMethod;
|
|
11
|
+
transactGet: DocumentClientMethod;
|
|
12
|
+
transactWrite: DocumentClientMethod;
|
|
13
|
+
} | {
|
|
14
|
+
send: (command: any) => Promise<any>;
|
|
15
|
+
}
|