wirejs-deploy-amplify-basic 0.0.68-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
  }
@@ -3,6 +3,6 @@
3
3
  "dependencies": {
4
4
  "jsdom": "^25.0.1",
5
5
  "wirejs-dom": "^1.0.38",
6
- "wirejs-resources": "^0.1.36-table-resource"
6
+ "wirejs-resources": "^0.1.37-table-resource"
7
7
  }
8
8
  }
@@ -1,6 +1,11 @@
1
1
  import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
- import { AnyKeyFieldOption, Filter, Parser, RecordKey, Resource } from 'wirejs-resources';
3
- export declare function PassThruParser<T>(record: Record<string, any>): T;
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>, const PK extends AnyKeyFieldOption<T>, const SK extends AnyKeyFieldOption<T> | undefined> extends Resource {
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
- partitionKey: PK;
19
- sort: SK | undefined;
23
+ key: Key;
24
+ indexes: Indexes | undefined;
20
25
  ddbClient: DynamoDBClient;
21
- table: string;
26
+ private table;
22
27
  constructor(scope: Resource | string, id: string, options: {
23
28
  parse: P;
24
- key: {
25
- partition: PK;
26
- sort?: SK;
27
- };
29
+ key: Key;
30
+ indexes?: Indexes;
28
31
  });
29
- get partitionKeyName(): PK['field'];
30
- get sortKeyName(): 'field' extends (keyof SK) ? SK['field'] : undefined;
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, PK, SK>): Promise<void>;
34
- deleteMany(items: (RecordKey<T, PK, SK>)[]): Promise<void>;
35
- get(key: RecordKey<T, PK, SK>): Promise<T | undefined>;
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(partition: Pick<T, PK['field']>, options?: {
40
- filter?: Filter<T>;
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,28 +91,29 @@ export function PassThruParser(record) {
94
91
  */
95
92
  export class DistributedTable extends Resource {
96
93
  parse;
97
- partitionKey;
98
- sort;
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.partitionKey = options.key.partition;
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
- addResource('DistributedTable', {
104
+ const resourceDefinition = {
109
105
  absoluteId: this.absoluteId,
110
- partitionKey: this.partitionKey,
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
- return this.partitionKey.field;
113
+ return this.key.partition.field;
116
114
  }
117
115
  get sortKeyName() {
118
- return this.sort?.field;
116
+ return this.key.sort?.field;
119
117
  }
120
118
  #getDDBKey(key) {
121
119
  const ddbKey = {
@@ -167,10 +165,10 @@ export class DistributedTable extends Resource {
167
165
  }
168
166
  async *scan(options = {}) {
169
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;
170
170
  let lastEvaluatedKey = undefined;
171
171
  do {
172
- const filterExpression = options.filter ? buildFilterExpression(options.filter) : undefined;
173
- const expressionAttributeValues = options.filter ? buildExpressionAttributeValues(options.filter) : undefined;
174
172
  const result = await this.ddbClient.send(new ScanCommand({
175
173
  TableName: this.table,
176
174
  FilterExpression: filterExpression,
@@ -186,27 +184,40 @@ export class DistributedTable extends Resource {
186
184
  lastEvaluatedKey = result.LastEvaluatedKey;
187
185
  } while (lastEvaluatedKey);
188
186
  }
189
- async *query(partition, options = {}) {
190
- console.log('Querying DynamoDB table:', this.table, partition, options);
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
+ });
191
206
  let lastEvaluatedKey = undefined;
192
207
  do {
193
- const ddbKey = this.#getDDBKey(partition);
194
- const filterExpression = options.filter ? buildFilterExpression(options.filter) : undefined;
195
- const expressionAttributeValues = options.filter ? buildExpressionAttributeValues(options.filter) : undefined;
196
208
  const result = await this.ddbClient.send(new QueryCommand({
197
209
  TableName: this.table,
198
- KeyConditionExpression: `${this.partitionKeyName} = :partitionKey`,
199
- ExpressionAttributeValues: {
200
- ':partitionKey': ddbKey[this.partitionKeyName],
201
- ...expressionAttributeValues
202
- },
210
+ IndexName: isIndexNameTheDefault ? undefined : options.by,
211
+ KeyConditionExpression: keyConditionExpression,
203
212
  FilterExpression: filterExpression,
213
+ ExpressionAttributeValues: expressionAttributeValues,
204
214
  ExclusiveStartKey: lastEvaluatedKey,
205
215
  }));
206
216
  for (const item of result.Items || []) {
207
217
  if (!item)
208
218
  continue;
209
- yield this.parse(item);
219
+ const record = this.parse(item);
220
+ yield record;
210
221
  }
211
222
  lastEvaluatedKey = result.LastEvaluatedKey;
212
223
  } while (lastEvaluatedKey);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wirejs-deploy-amplify-basic",
3
- "version": "0.0.68-table-resource",
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.36-table-resource"
35
+ "wirejs-resources": "^0.1.37-table-resource"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@aws-amplify/backend": "^1.14.0",