wirejs-deploy-amplify-basic 0.0.67-table-resource → 0.0.69-table-resource
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.
|
@@ -8,6 +8,7 @@ import { Table, AttributeType, BillingMode } from 'aws-cdk-lib/aws-dynamodb';
|
|
|
8
8
|
import { api } from './functions/api/resource';
|
|
9
9
|
import { auth } from './auth/resource';
|
|
10
10
|
import { KeyFieldDefinition } from 'wirejs-resources';
|
|
11
|
+
import type { TableDefinition } from '../wirejs-resources-overrides/resources/distributed-table';
|
|
11
12
|
|
|
12
13
|
// @ts-ignore
|
|
13
14
|
import generated from './generated-resources';
|
|
@@ -58,11 +59,7 @@ bucket.grantReadWrite(backend.api.resources.lambda);
|
|
|
58
59
|
*/
|
|
59
60
|
function isDistributedTable(resource: any): resource is {
|
|
60
61
|
type: 'DistributedTable';
|
|
61
|
-
options:
|
|
62
|
-
absoluteId: string;
|
|
63
|
-
partitionKey: KeyFieldDefinition<any, any, any>;
|
|
64
|
-
sortKey: KeyFieldDefinition<any, any, any> | undefined;
|
|
65
|
-
}
|
|
62
|
+
options: TableDefinition;
|
|
66
63
|
} {
|
|
67
64
|
return resource.type === 'DistributedTable';
|
|
68
65
|
}
|
|
@@ -76,6 +73,7 @@ const PKFieldTypes = {
|
|
|
76
73
|
for (const resource of generated) {
|
|
77
74
|
if (isDistributedTable(resource)) {
|
|
78
75
|
const sanitizedId = resource.options.absoluteId.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
76
|
+
|
|
79
77
|
const table = new Table(backend.stack, sanitizedId, {
|
|
80
78
|
partitionKey: {
|
|
81
79
|
name: resource.options.partitionKey.field,
|
|
@@ -90,6 +88,22 @@ for (const resource of generated) {
|
|
|
90
88
|
billingMode: BillingMode.PAY_PER_REQUEST,
|
|
91
89
|
pointInTimeRecovery: true,
|
|
92
90
|
});
|
|
91
|
+
|
|
92
|
+
for (const index of resource.options.indexes ?? []) {
|
|
93
|
+
const gsi = table.addGlobalSecondaryIndex({
|
|
94
|
+
indexName: `by${index.partition.field}And${index.sort?.field ?? 'None'}`,
|
|
95
|
+
partitionKey: {
|
|
96
|
+
name: index.partition.field,
|
|
97
|
+
type: PKFieldTypes[index.partition.type],
|
|
98
|
+
},
|
|
99
|
+
sortKey: index.sort ? {
|
|
100
|
+
name: index.sort.field,
|
|
101
|
+
type: PKFieldTypes[index.sort.type],
|
|
102
|
+
} : undefined,
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
table.grantReadWriteData(backend.api.resources.lambda);
|
|
94
108
|
}
|
|
95
109
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
2
|
-
import {
|
|
3
|
-
export
|
|
2
|
+
import { AllIndexesByName, KeyCondition, IndexFieldNames, Filter, Index, KeyFieldDefinition, KindaPretty, Parser, RecordKey, Resource, DistributedTable as BaseDistributedTable } from 'wirejs-resources';
|
|
3
|
+
export type TableDefinition = {
|
|
4
|
+
absoluteId: string;
|
|
5
|
+
partitionKey: KeyFieldDefinition;
|
|
6
|
+
sortKey?: KeyFieldDefinition;
|
|
7
|
+
indexes?: Index<any>[];
|
|
8
|
+
};
|
|
4
9
|
/**
|
|
5
10
|
* A table of records that favors very high *overall* scalability at the expense of
|
|
6
11
|
* scalability *between* partitions. Providers will distribute your data across many
|
|
@@ -12,31 +17,31 @@ export declare function PassThruParser<T>(record: Record<string, any>): T;
|
|
|
12
17
|
*
|
|
13
18
|
* High cardinality, non-sequential partition keys allow for the best overall scaling.
|
|
14
19
|
*/
|
|
15
|
-
export declare class DistributedTable<const P extends Parser<any>, const T extends ReturnType<P
|
|
20
|
+
export declare class DistributedTable<const P extends Parser<any>, const T extends KindaPretty<ReturnType<P>>, const Key extends Index<T>, const Indexes extends Index<T>[] | undefined = undefined> extends Resource implements Omit<BaseDistributedTable<P, T, Key, Indexes>, '#private'> {
|
|
16
21
|
#private;
|
|
17
22
|
parse: P;
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
key: Key;
|
|
24
|
+
indexes: Indexes | undefined;
|
|
20
25
|
ddbClient: DynamoDBClient;
|
|
21
|
-
table
|
|
26
|
+
private table;
|
|
22
27
|
constructor(scope: Resource | string, id: string, options: {
|
|
23
28
|
parse: P;
|
|
24
|
-
key:
|
|
25
|
-
|
|
26
|
-
sort?: SK;
|
|
27
|
-
};
|
|
29
|
+
key: Key;
|
|
30
|
+
indexes?: Indexes;
|
|
28
31
|
});
|
|
29
|
-
get partitionKeyName():
|
|
30
|
-
get sortKeyName(): 'field' extends (
|
|
32
|
+
get partitionKeyName(): Key['partition']['field'];
|
|
33
|
+
get sortKeyName(): 'field' extends keyof Key['sort'] ? (Key['sort']['field'] extends string ? Key['sort']['field'] : undefined) : undefined;
|
|
31
34
|
save(item: T): Promise<void>;
|
|
32
35
|
saveMany(items: T[]): Promise<void>;
|
|
33
|
-
delete(item: RecordKey<T,
|
|
34
|
-
deleteMany(items: (RecordKey<T,
|
|
35
|
-
get(key: RecordKey<T,
|
|
36
|
+
delete(item: RecordKey<T, Key>): Promise<void>;
|
|
37
|
+
deleteMany(items: (RecordKey<T, Key>)[]): Promise<void>;
|
|
38
|
+
get(key: RecordKey<T, Key>): Promise<T | undefined>;
|
|
36
39
|
scan(options?: {
|
|
37
40
|
filter?: Filter<T>;
|
|
38
41
|
}): AsyncGenerator<T>;
|
|
39
|
-
query
|
|
40
|
-
|
|
42
|
+
query<const GivenPartition extends keyof AllIndexesByName<BaseDistributedTable<P, T, Key, Indexes>> & string>(options: {
|
|
43
|
+
by: GivenPartition;
|
|
44
|
+
where: KeyCondition<BaseDistributedTable<P, T, Key, Indexes>, GivenPartition>;
|
|
45
|
+
filter?: Filter<Omit<T, IndexFieldNames<BaseDistributedTable<P, T, Key, Indexes>, GivenPartition> & string>>;
|
|
41
46
|
}): AsyncGenerator<T>;
|
|
42
47
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { env } from 'process';
|
|
2
2
|
import { DynamoDBClient, } from '@aws-sdk/client-dynamodb';
|
|
3
3
|
import { PutCommand, GetCommand, QueryCommand, ScanCommand, DeleteCommand, } from '@aws-sdk/lib-dynamodb';
|
|
4
|
-
import { Resource, } from 'wirejs-resources';
|
|
4
|
+
import { Resource, indexName, } from 'wirejs-resources';
|
|
5
5
|
import { addResource } from '../resource-collector.js';
|
|
6
6
|
function isFieldComparison(filter) {
|
|
7
7
|
return !['and', 'or', 'not'].some(key => key in filter);
|
|
@@ -78,9 +78,6 @@ function buildExpressionAttributeValues(filter) {
|
|
|
78
78
|
}
|
|
79
79
|
return values;
|
|
80
80
|
}
|
|
81
|
-
export function PassThruParser(record) {
|
|
82
|
-
return record;
|
|
83
|
-
}
|
|
84
81
|
/**
|
|
85
82
|
* A table of records that favors very high *overall* scalability at the expense of
|
|
86
83
|
* scalability *between* partitions. Providers will distribute your data across many
|
|
@@ -94,30 +91,29 @@ export function PassThruParser(record) {
|
|
|
94
91
|
*/
|
|
95
92
|
export class DistributedTable extends Resource {
|
|
96
93
|
parse;
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
key;
|
|
95
|
+
indexes;
|
|
99
96
|
ddbClient;
|
|
100
97
|
table;
|
|
101
98
|
constructor(scope, id, options) {
|
|
102
99
|
super(scope, id);
|
|
103
100
|
this.parse = options.parse;
|
|
104
|
-
this.
|
|
105
|
-
this.sort = options.key.sort;
|
|
101
|
+
this.key = options.key;
|
|
106
102
|
this.ddbClient = new DynamoDBClient();
|
|
107
103
|
this.table = env['TABLE_NAME_PREFIX'] + this.absoluteId.replace(/[^a-zA-Z0-9-_]/g, '_');
|
|
108
|
-
|
|
104
|
+
const resourceDefinition = {
|
|
109
105
|
absoluteId: this.absoluteId,
|
|
110
|
-
partitionKey: this.
|
|
111
|
-
sortKey: this.sort,
|
|
112
|
-
|
|
106
|
+
partitionKey: this.key.partition,
|
|
107
|
+
sortKey: this.key.sort,
|
|
108
|
+
indexes: this.indexes
|
|
109
|
+
};
|
|
110
|
+
addResource('DistributedTable', resourceDefinition);
|
|
113
111
|
}
|
|
114
112
|
get partitionKeyName() {
|
|
115
|
-
|
|
116
|
-
return pkField;
|
|
113
|
+
return this.key.partition.field;
|
|
117
114
|
}
|
|
118
115
|
get sortKeyName() {
|
|
119
|
-
|
|
120
|
-
return skField;
|
|
116
|
+
return this.key.sort?.field;
|
|
121
117
|
}
|
|
122
118
|
#getDDBKey(key) {
|
|
123
119
|
const ddbKey = {
|
|
@@ -169,10 +165,10 @@ export class DistributedTable extends Resource {
|
|
|
169
165
|
}
|
|
170
166
|
async *scan(options = {}) {
|
|
171
167
|
console.log('Scanning DynamoDB table:', this.table, options);
|
|
168
|
+
const filterExpression = options.filter ? buildFilterExpression(options.filter) : undefined;
|
|
169
|
+
const expressionAttributeValues = options.filter ? buildExpressionAttributeValues(options.filter) : undefined;
|
|
172
170
|
let lastEvaluatedKey = undefined;
|
|
173
171
|
do {
|
|
174
|
-
const filterExpression = options.filter ? buildFilterExpression(options.filter) : undefined;
|
|
175
|
-
const expressionAttributeValues = options.filter ? buildExpressionAttributeValues(options.filter) : undefined;
|
|
176
172
|
const result = await this.ddbClient.send(new ScanCommand({
|
|
177
173
|
TableName: this.table,
|
|
178
174
|
FilterExpression: filterExpression,
|
|
@@ -188,27 +184,40 @@ export class DistributedTable extends Resource {
|
|
|
188
184
|
lastEvaluatedKey = result.LastEvaluatedKey;
|
|
189
185
|
} while (lastEvaluatedKey);
|
|
190
186
|
}
|
|
191
|
-
async *query(
|
|
192
|
-
console.log('Querying DynamoDB table:', this.table,
|
|
187
|
+
async *query(options) {
|
|
188
|
+
console.log('Querying DynamoDB table:', this.table, options);
|
|
189
|
+
// Build the key condition expression from the `where` clause
|
|
190
|
+
const whereClauseAsFilter = {
|
|
191
|
+
// decompose each `where` clause property into a separate condition.
|
|
192
|
+
and: Object.entries(options.where).map(([k, v]) => buildFilterExpression({ [k]: v }))
|
|
193
|
+
};
|
|
194
|
+
const keyConditionExpression = buildFilterExpression(whereClauseAsFilter);
|
|
195
|
+
// Build the filter expression if provided
|
|
196
|
+
const filterExpression = options.filter ? buildFilterExpression(options.filter) : undefined;
|
|
197
|
+
// Combine expression attribute values for both key conditions and filters
|
|
198
|
+
const expressionAttributeValues = {
|
|
199
|
+
...buildExpressionAttributeValues(whereClauseAsFilter),
|
|
200
|
+
...(options.filter ? buildExpressionAttributeValues(options.filter) : {}),
|
|
201
|
+
};
|
|
202
|
+
const isIndexNameTheDefault = options.by === indexName({
|
|
203
|
+
partition: this.key.partition,
|
|
204
|
+
sort: this.key.sort
|
|
205
|
+
});
|
|
193
206
|
let lastEvaluatedKey = undefined;
|
|
194
207
|
do {
|
|
195
|
-
const ddbKey = this.#getDDBKey(partition);
|
|
196
|
-
const filterExpression = options.filter ? buildFilterExpression(options.filter) : undefined;
|
|
197
|
-
const expressionAttributeValues = options.filter ? buildExpressionAttributeValues(options.filter) : undefined;
|
|
198
208
|
const result = await this.ddbClient.send(new QueryCommand({
|
|
199
209
|
TableName: this.table,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
':partitionKey': ddbKey[this.partitionKeyName],
|
|
203
|
-
...expressionAttributeValues
|
|
204
|
-
},
|
|
210
|
+
IndexName: isIndexNameTheDefault ? undefined : options.by,
|
|
211
|
+
KeyConditionExpression: keyConditionExpression,
|
|
205
212
|
FilterExpression: filterExpression,
|
|
213
|
+
ExpressionAttributeValues: expressionAttributeValues,
|
|
206
214
|
ExclusiveStartKey: lastEvaluatedKey,
|
|
207
215
|
}));
|
|
208
216
|
for (const item of result.Items || []) {
|
|
209
217
|
if (!item)
|
|
210
218
|
continue;
|
|
211
|
-
|
|
219
|
+
const record = this.parse(item);
|
|
220
|
+
yield record;
|
|
212
221
|
}
|
|
213
222
|
lastEvaluatedKey = result.LastEvaluatedKey;
|
|
214
223
|
} while (lastEvaluatedKey);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wirejs-deploy-amplify-basic",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.69-table-resource",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"recursive-copy": "^2.0.14",
|
|
33
33
|
"rimraf": "^6.0.1",
|
|
34
34
|
"wirejs-dom": "^1.0.38",
|
|
35
|
-
"wirejs-resources": "^0.1.
|
|
35
|
+
"wirejs-resources": "^0.1.37-table-resource"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@aws-amplify/backend": "^1.14.0",
|