triangle-utils 1.4.9 → 1.4.10

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.
@@ -1,6 +1,7 @@
1
1
  export declare class UtilsDynamoDB {
2
2
  private readonly dynamodb;
3
3
  constructor(region: string);
4
+ get_table_key_names(table_name: string): Promise<string[] | undefined>;
4
5
  scan(table: string, options?: {
5
6
  filters?: Record<string, any>;
6
7
  undefined_attribute_names?: string[];
@@ -10,27 +11,30 @@ export declare class UtilsDynamoDB {
10
11
  }): Promise<Record<string, any>[]>;
11
12
  get(table: string, key: Record<string, any>, consistent?: boolean): Promise<Record<string, any> | undefined>;
12
13
  get_max(table: string, primary_key: Record<string, any>): Promise<Record<string, any> | undefined>;
13
- query(table: string, primary_key: Record<string, any>, options?: {
14
- index_name?: string;
14
+ query(table_index_name: string, primary_key: Record<string, any>, options?: {
15
15
  reverse?: boolean;
16
16
  compile?: boolean;
17
+ project?: boolean;
18
+ attribute_names?: string[];
17
19
  }): Promise<Record<string, any>[]>;
18
- query_prefix(table: string, primary_key: Record<string, any>, secondary_key_prefix: Record<string, string>, options?: {
19
- index_name?: string;
20
+ query_prefix(table_index_name: string, primary_key: Record<string, any>, secondary_key_prefix: Record<string, string>, options?: {
20
21
  reverse?: boolean;
21
22
  compile?: boolean;
23
+ project?: boolean;
24
+ attribute_names?: string[];
22
25
  }): Promise<Record<string, any>[]>;
23
- query_range(table: string, primary_key: Record<string, any>, secondary_key_range: Record<string, (string | number)[]>, options?: {
24
- index_name?: string;
26
+ query_range(table_index_name: string, primary_key: Record<string, any>, secondary_key_range: Record<string, (string | number)[]>, options?: {
25
27
  reverse?: boolean;
26
28
  compile?: boolean;
29
+ project?: boolean;
30
+ attribute_names?: string[];
27
31
  }): Promise<Record<string, any>[]>;
28
- set(table: string, key: Record<string, any>, attributes: Record<string, any>): Promise<void>;
29
- append(table: string, key: Record<string, any>, attributes: Record<string, any[]>): Promise<void>;
30
- add(table: string, key: Record<string, any>, attributes: Record<string, any[]>): Promise<void>;
31
- remove(table: string, key: Record<string, any>, attributes: string[]): Promise<void>;
32
- create(table: string, key: Record<string, any>, attributes?: Record<string, any>): Promise<void>;
33
- delete(table: string, key: Record<string, any>): Promise<void>;
34
- duplicate_attribute(table: string, attribute_name: string, new_attribute_name: string): Promise<void>;
35
- remove_attribute(table: string, attribute_name: string): Promise<void>;
32
+ set(table_name: string, key: Record<string, any>, attributes: Record<string, any>): Promise<void>;
33
+ append(table_name: string, key: Record<string, any>, attributes: Record<string, any[]>): Promise<void>;
34
+ add(table_name: string, key: Record<string, any>, attributes: Record<string, any[]>): Promise<void>;
35
+ remove(table_name: string, key: Record<string, any>, attributes: string[]): Promise<void>;
36
+ create(table_name: string, key: Record<string, any>, attributes?: Record<string, any>): Promise<void>;
37
+ delete(table_name: string, key: Record<string, any>): Promise<void>;
38
+ duplicate_attribute(table_name: string, attribute_name: string, new_attribute_name: string): Promise<void>;
39
+ remove_attribute(table_name: string, attribute_name: string): Promise<undefined>;
36
40
  }
@@ -87,6 +87,17 @@ export class UtilsDynamoDB {
87
87
  constructor(region) {
88
88
  this.dynamodb = new DynamoDB({ region: region });
89
89
  }
90
+ async get_table_key_names(table_name) {
91
+ const table_metadata = await this.dynamodb.describeTable({
92
+ TableName: table_name
93
+ });
94
+ if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
95
+ return undefined;
96
+ }
97
+ const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
98
+ .filter(table_key_name => table_key_name !== undefined);
99
+ return table_key_names;
100
+ }
90
101
  async scan(table, options = {}) {
91
102
  const filters = options.filters !== undefined ? options.filters : {};
92
103
  const undefined_attribute_names = options.undefined_attribute_names !== undefined ? options.undefined_attribute_names : [];
@@ -173,11 +184,15 @@ export class UtilsDynamoDB {
173
184
  }
174
185
  return converted_output;
175
186
  }
176
- async query(table, primary_key, options = {}) {
177
- const index_name = options.index_name !== undefined ? options.index_name : undefined;
187
+ async query(table_index_name, primary_key, options = {}) {
188
+ const table_name = table_index_name.split(":")[0];
189
+ const index_name = table_index_name.split(":")[1];
178
190
  const reverse = options.reverse !== undefined ? options.reverse : false;
179
191
  const compile = options.compile !== undefined ? options.compile : true;
180
- if (Object.keys(primary_key).length !== 1) {
192
+ const project = options.compile !== undefined ? options.project : true;
193
+ const attribute_names = options.attribute_names !== undefined ? options.attribute_names : [];
194
+ const table_key_names = await this.get_table_key_names(table_name);
195
+ if (table_key_names === undefined || Object.keys(primary_key).length !== 1) {
181
196
  return [];
182
197
  }
183
198
  const key = Object.keys(primary_key)[0];
@@ -185,8 +200,9 @@ export class UtilsDynamoDB {
185
200
  if (value === undefined) {
186
201
  return [];
187
202
  }
203
+ const projection_expression = attribute_names.map(attribute_name => "#" + attribute_name).join(", ");
188
204
  const request = {
189
- TableName: table,
205
+ TableName: table_name,
190
206
  IndexName: index_name,
191
207
  ExpressionAttributeNames: {
192
208
  "#a": key
@@ -194,16 +210,27 @@ export class UtilsDynamoDB {
194
210
  ExpressionAttributeValues: {
195
211
  ":a": value
196
212
  },
213
+ ProjectionExpression: projection_expression.length > 0 ? projection_expression : undefined,
197
214
  KeyConditionExpression: "#a = :a",
198
215
  ScanIndexForward: !reverse
199
216
  };
200
- return await compile_pages(request, (request) => this.dynamodb.query(request), compile);
217
+ const items = await compile_pages(request, (request) => this.dynamodb.query(request), compile)
218
+ .then(async (items) => index_name === undefined || !project ? items :
219
+ await Promise.all(items.map(async (item) => {
220
+ return await this.get(table_name, Object.fromEntries(Object.entries(item).filter((([key_name, key_value]) => table_key_names.includes(key_name)))));
221
+ }))
222
+ .then(items => items.filter(item => item !== undefined)));
223
+ return items;
201
224
  }
202
- async query_prefix(table, primary_key, secondary_key_prefix, options = {}) {
203
- const index_name = options.index_name !== undefined ? options.index_name : undefined;
225
+ async query_prefix(table_index_name, primary_key, secondary_key_prefix, options = {}) {
226
+ const table_name = table_index_name.split(":")[0];
227
+ const index_name = table_index_name.split(":")[1];
204
228
  const reverse = options.reverse !== undefined ? options.reverse : false;
205
229
  const compile = options.compile !== undefined ? options.compile : true;
206
- if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_prefix).length !== 1) {
230
+ const project = options.compile !== undefined ? options.project : true;
231
+ const attribute_names = options.attribute_names !== undefined ? options.attribute_names : [];
232
+ const table_key_names = await this.get_table_key_names(table_name);
233
+ if (table_key_names === undefined || Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_prefix).length !== 1) {
207
234
  return [];
208
235
  }
209
236
  const converted_primary_value = convert_input(Object.values(primary_key)[0]);
@@ -211,8 +238,9 @@ export class UtilsDynamoDB {
211
238
  if (converted_primary_value === undefined || converted_secondary_prefix_value === undefined) {
212
239
  return [];
213
240
  }
241
+ const projection_expression = attribute_names.map(attribute_name => "#" + attribute_name).join(", ");
214
242
  const request = {
215
- TableName: table,
243
+ TableName: table_name,
216
244
  IndexName: index_name,
217
245
  ExpressionAttributeNames: {
218
246
  "#a": Object.keys(primary_key)[0],
@@ -222,16 +250,27 @@ export class UtilsDynamoDB {
222
250
  ":a": converted_primary_value,
223
251
  ":b": converted_secondary_prefix_value
224
252
  },
253
+ ProjectionExpression: projection_expression.length > 0 ? projection_expression : undefined,
225
254
  KeyConditionExpression: "#a = :a AND begins_with(#b, :b)",
226
255
  ScanIndexForward: !reverse
227
256
  };
228
- return await compile_pages(request, (request) => this.dynamodb.query(request), compile);
257
+ const items = await compile_pages(request, (request) => this.dynamodb.query(request), compile)
258
+ .then(async (items) => index_name === undefined || !project ? items :
259
+ await Promise.all(items.map(async (item) => {
260
+ return await this.get(table_name, Object.fromEntries(Object.entries(item).filter((([key_name, key_value]) => table_key_names.includes(key_name)))));
261
+ }))
262
+ .then(items => items.filter(item => item !== undefined)));
263
+ return items;
229
264
  }
230
- async query_range(table, primary_key, secondary_key_range, options = {}) {
231
- const index_name = options.index_name !== undefined ? options.index_name : undefined;
265
+ async query_range(table_index_name, primary_key, secondary_key_range, options = {}) {
266
+ const table_name = table_index_name.split(":")[0];
267
+ const index_name = table_index_name.split(":")[1];
232
268
  const reverse = options.reverse !== undefined ? options.reverse : false;
233
269
  const compile = options.compile !== undefined ? options.compile : true;
234
- if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_range).length !== 1 || Object.values(secondary_key_range)[0].length !== 2) {
270
+ const project = options.compile !== undefined ? options.project : true;
271
+ const attribute_names = options.attribute_names !== undefined ? options.attribute_names : [];
272
+ const table_key_names = await this.get_table_key_names(table_name);
273
+ if (table_key_names === undefined || Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_range).length !== 1 || Object.values(secondary_key_range)[0].length !== 2) {
235
274
  return [];
236
275
  }
237
276
  const converted_primary_value = convert_input(Object.values(primary_key)[0]);
@@ -240,8 +279,9 @@ export class UtilsDynamoDB {
240
279
  if (converted_primary_value === undefined || converted_secondary_range_start_value === undefined || converted_secondary_range_end_value === undefined) {
241
280
  return [];
242
281
  }
282
+ const projection_expression = attribute_names.map(attribute_name => "#" + attribute_name).join(", ");
243
283
  const request = {
244
- TableName: table,
284
+ TableName: table_name,
245
285
  IndexName: index_name,
246
286
  ExpressionAttributeNames: {
247
287
  "#a": Object.keys(primary_key)[0],
@@ -252,15 +292,22 @@ export class UtilsDynamoDB {
252
292
  ":b1": converted_secondary_range_start_value,
253
293
  ":b2": converted_secondary_range_end_value
254
294
  },
295
+ ProjectionExpression: projection_expression.length > 0 ? projection_expression : undefined,
255
296
  KeyConditionExpression: "#a = :a AND (#b BETWEEN :b1 AND :b2)",
256
297
  ScanIndexForward: !reverse
257
298
  };
258
- return await compile_pages(request, (request) => this.dynamodb.query(request), compile);
299
+ const items = await compile_pages(request, (request) => this.dynamodb.query(request), compile)
300
+ .then(async (items) => index_name === undefined || !project ? items :
301
+ await Promise.all(items.map(async (item) => {
302
+ return await this.get(table_name, Object.fromEntries(Object.entries(item).filter((([key_name, key_value]) => table_key_names.includes(key_name)))));
303
+ }))
304
+ .then(items => items.filter(item => item !== undefined)));
305
+ return items;
259
306
  }
260
- async set(table, key, attributes) {
307
+ async set(table_name, key, attributes) {
261
308
  const converted_key = convert_input(key)?.M;
262
309
  const request = {
263
- TableName: table,
310
+ TableName: table_name,
264
311
  Key: converted_key,
265
312
  UpdateExpression: "set " + Object.keys(attributes)
266
313
  .filter(attribute_name => !Object.keys(key).includes(attribute_name))
@@ -277,10 +324,10 @@ export class UtilsDynamoDB {
277
324
  };
278
325
  await this.dynamodb.updateItem(request);
279
326
  }
280
- async append(table, key, attributes) {
327
+ async append(table_name, key, attributes) {
281
328
  const converted_key = convert_input(key)?.M;
282
329
  const request = {
283
- TableName: table,
330
+ TableName: table_name,
284
331
  Key: converted_key,
285
332
  UpdateExpression: "set " + Object.keys(attributes)
286
333
  .filter(attribute_name => attributes[attribute_name] !== undefined)
@@ -295,8 +342,8 @@ export class UtilsDynamoDB {
295
342
  };
296
343
  this.dynamodb.updateItem(request);
297
344
  }
298
- async add(table, key, attributes) {
299
- const item = await this.get(table, key, true);
345
+ async add(table_name, key, attributes) {
346
+ const item = await this.get(table_name, key, true);
300
347
  if (item === undefined) {
301
348
  return;
302
349
  }
@@ -313,12 +360,12 @@ export class UtilsDynamoDB {
313
360
  if (Object.values(new_attributes).flat().length === 0) {
314
361
  return undefined;
315
362
  }
316
- return await this.append(table, key, attributes);
363
+ return await this.append(table_name, key, attributes);
317
364
  }
318
- async remove(table, key, attributes) {
365
+ async remove(table_name, key, attributes) {
319
366
  const converted_key = convert_input(key)?.M;
320
367
  const request = {
321
- TableName: table,
368
+ TableName: table_name,
322
369
  Key: converted_key,
323
370
  UpdateExpression: "remove " + attributes
324
371
  .map(attribute_name => "#" + attribute_name).join(", "),
@@ -327,35 +374,35 @@ export class UtilsDynamoDB {
327
374
  };
328
375
  await this.dynamodb.updateItem(request);
329
376
  }
330
- async create(table, key, attributes = {}) {
331
- const item = await this.get(table, key, true);
377
+ async create(table_name, key, attributes = {}) {
378
+ const item = await this.get(table_name, key, true);
332
379
  if (item !== undefined) {
333
380
  return;
334
381
  }
335
382
  const converted_key = convert_input(key)?.M;
336
383
  const converted_attributes = convert_input(attributes)?.M;
337
384
  await this.dynamodb.putItem({
338
- TableName: table,
385
+ TableName: table_name,
339
386
  Item: { ...converted_key, ...converted_attributes }
340
387
  });
341
388
  }
342
- async delete(table, key) {
389
+ async delete(table_name, key) {
343
390
  const converted_key = convert_input(key)?.M;
344
391
  await this.dynamodb.deleteItem({
345
- TableName: table,
392
+ TableName: table_name,
346
393
  Key: converted_key
347
394
  });
348
395
  }
349
- async duplicate_attribute(table, attribute_name, new_attribute_name) {
396
+ async duplicate_attribute(table_name, attribute_name, new_attribute_name) {
350
397
  const table_metadata = await this.dynamodb.describeTable({
351
- TableName: table
398
+ TableName: table_name
352
399
  });
353
400
  if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
354
401
  return;
355
402
  }
356
403
  const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
357
404
  .filter(table_key_name => table_key_name !== undefined);
358
- const items = await this.scan(table, { attribute_names: table_key_names.concat([attribute_name, new_attribute_name]) });
405
+ const items = await this.scan(table_name, { attribute_names: table_key_names.concat([attribute_name, new_attribute_name]) });
359
406
  if (items.filter(item => item[new_attribute_name] !== undefined).length > 0) {
360
407
  console.log("Cannot rename.", new_attribute_name, "is an existing item.");
361
408
  return;
@@ -365,23 +412,19 @@ export class UtilsDynamoDB {
365
412
  continue;
366
413
  }
367
414
  const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]));
368
- await this.set(table, key, { [new_attribute_name]: item[attribute_name] });
415
+ await this.set(table_name, key, { [new_attribute_name]: item[attribute_name] });
369
416
  }
370
417
  }
371
- async remove_attribute(table, attribute_name) {
372
- const table_metadata = await this.dynamodb.describeTable({
373
- TableName: table
374
- });
375
- if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
376
- return;
418
+ async remove_attribute(table_name, attribute_name) {
419
+ const table_key_names = await this.get_table_key_names(table_name);
420
+ if (table_key_names === undefined) {
421
+ return undefined;
377
422
  }
378
- const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
379
- .filter(table_key_name => table_key_name !== undefined);
380
- const items = await this.scan(table)
423
+ const items = await this.scan(table_name)
381
424
  .then(items => items.filter(item => item[attribute_name] !== undefined));
382
425
  for (const item of items) {
383
426
  const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]));
384
- await this.remove(table, key, [attribute_name]);
427
+ await this.remove(table_name, key, [attribute_name]);
385
428
  }
386
429
  }
387
430
  }
@@ -0,0 +1 @@
1
+ export {};
package/dist/src/f.js ADDED
@@ -0,0 +1,17 @@
1
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager";
2
+ import { TriangleUtils } from "./index.js";
3
+ const secret_name = "triage_config";
4
+ const secrets_manager = new SecretsManager({ region: "us-east-1" });
5
+ const response = await secrets_manager.getSecretValue({
6
+ SecretId: secret_name
7
+ });
8
+ const api_keys = JSON.parse(response?.SecretString || "");
9
+ const config = {
10
+ google_email: "louishou@triangleanalytics.com",
11
+ alerts_email: "alerts@triangleanalytics.com",
12
+ region: "us-east-1",
13
+ ...api_keys
14
+ };
15
+ const utils = new TriangleUtils(config);
16
+ const foods = await utils.dynamodb.query("triage_docket_documents:register_document_id", { register_document_id: "E6-17065" });
17
+ console.log(foods);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triangle-utils",
3
- "version": "1.4.9",
3
+ "version": "1.4.10",
4
4
  "main": "dist/src/index.js",
5
5
  "directories": {
6
6
  "test": "vitest"
@@ -91,6 +91,20 @@ export class UtilsDynamoDB {
91
91
  this.dynamodb = new DynamoDB({ region : region })
92
92
  }
93
93
 
94
+ async get_table_key_names(
95
+ table_name : string
96
+ ) : Promise<string[] | undefined> {
97
+ const table_metadata = await this.dynamodb.describeTable({
98
+ TableName : table_name
99
+ })
100
+ if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
101
+ return undefined
102
+ }
103
+ const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
104
+ .filter(table_key_name => table_key_name !== undefined)
105
+ return table_key_names
106
+ }
107
+
94
108
  async scan(
95
109
  table : string,
96
110
  options : {
@@ -201,18 +215,24 @@ export class UtilsDynamoDB {
201
215
  }
202
216
 
203
217
  async query(
204
- table : string,
218
+ table_index_name : string,
205
219
  primary_key : Record<string, any>,
206
220
  options : {
207
- index_name? : string,
208
221
  reverse? : boolean,
209
- compile? : boolean
222
+ compile? : boolean,
223
+ project? : boolean,
224
+ attribute_names? : string[]
210
225
  } = {}
211
226
  ) : Promise<Record<string, any>[]>{
212
- const index_name = options.index_name !== undefined ? options.index_name : undefined
227
+ const table_name : string = table_index_name.split(":")[0]
228
+ const index_name : string | undefined = table_index_name.split(":")[1]
213
229
  const reverse = options.reverse !== undefined ? options.reverse : false
214
230
  const compile = options.compile !== undefined ? options.compile : true
215
- if (Object.keys(primary_key).length !== 1) {
231
+ const project = options.compile !== undefined ? options.project : true
232
+ const attribute_names : string[] = options.attribute_names !== undefined ? options.attribute_names : []
233
+
234
+ const table_key_names = await this.get_table_key_names(table_name)
235
+ if (table_key_names === undefined || Object.keys(primary_key).length !== 1) {
216
236
  return []
217
237
  }
218
238
  const key = Object.keys(primary_key)[0]
@@ -220,8 +240,9 @@ export class UtilsDynamoDB {
220
240
  if (value === undefined) {
221
241
  return []
222
242
  }
243
+ const projection_expression = attribute_names.map(attribute_name => "#" + attribute_name).join(", ")
223
244
  const request : QueryCommandInput = {
224
- TableName : table,
245
+ TableName : table_name,
225
246
  IndexName : index_name,
226
247
  ExpressionAttributeNames: {
227
248
  "#a": key
@@ -229,26 +250,42 @@ export class UtilsDynamoDB {
229
250
  ExpressionAttributeValues: {
230
251
  ":a": value
231
252
  },
253
+ ProjectionExpression : projection_expression.length > 0 ? projection_expression : undefined,
232
254
  KeyConditionExpression: "#a = :a",
233
255
  ScanIndexForward : !reverse
234
256
  }
235
- return await compile_pages(request, (request) => this.dynamodb.query(request), compile)
257
+ const items = await compile_pages(request, (request) => this.dynamodb.query(request), compile)
258
+ .then(async items => index_name === undefined || !project ? items :
259
+ await Promise.all(items.map(async item => {
260
+ return await this.get(table_name,
261
+ Object.fromEntries(Object.entries(item).filter((([key_name, key_value]) => table_key_names.includes(key_name))))
262
+ )
263
+ }))
264
+ .then(items => items.filter(item => item !== undefined))
265
+ )
266
+ return items
236
267
  }
237
268
 
238
269
  async query_prefix(
239
- table : string,
270
+ table_index_name : string,
240
271
  primary_key : Record<string, any>,
241
272
  secondary_key_prefix : Record<string, string>,
242
273
  options : {
243
- index_name? : string,
244
274
  reverse? : boolean,
245
- compile? : boolean
275
+ compile? : boolean,
276
+ project? : boolean,
277
+ attribute_names? : string[]
246
278
  } = {}
247
279
  ) : Promise<Record<string, any>[]>{
248
- const index_name = options.index_name !== undefined ? options.index_name : undefined
280
+ const table_name : string = table_index_name.split(":")[0]
281
+ const index_name : string | undefined = table_index_name.split(":")[1]
249
282
  const reverse = options.reverse !== undefined ? options.reverse : false
250
283
  const compile = options.compile !== undefined ? options.compile : true
251
- if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_prefix).length !== 1) {
284
+ const project = options.compile !== undefined ? options.project : true
285
+ const attribute_names : string[] = options.attribute_names !== undefined ? options.attribute_names : []
286
+
287
+ const table_key_names = await this.get_table_key_names(table_name)
288
+ if (table_key_names === undefined || Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_prefix).length !== 1) {
252
289
  return []
253
290
  }
254
291
  const converted_primary_value = convert_input(Object.values(primary_key)[0])
@@ -256,8 +293,9 @@ export class UtilsDynamoDB {
256
293
  if (converted_primary_value === undefined || converted_secondary_prefix_value === undefined) {
257
294
  return []
258
295
  }
296
+ const projection_expression = attribute_names.map(attribute_name => "#" + attribute_name).join(", ")
259
297
  const request : QueryCommandInput = {
260
- TableName : table,
298
+ TableName : table_name,
261
299
  IndexName : index_name,
262
300
  ExpressionAttributeNames: {
263
301
  "#a": Object.keys(primary_key)[0],
@@ -267,26 +305,42 @@ export class UtilsDynamoDB {
267
305
  ":a": converted_primary_value,
268
306
  ":b": converted_secondary_prefix_value
269
307
  },
308
+ ProjectionExpression : projection_expression.length > 0 ? projection_expression : undefined,
270
309
  KeyConditionExpression: "#a = :a AND begins_with(#b, :b)",
271
310
  ScanIndexForward : !reverse
272
311
  }
273
- return await compile_pages(request, (request) => this.dynamodb.query(request), compile)
312
+ const items = await compile_pages(request, (request) => this.dynamodb.query(request), compile)
313
+ .then(async items => index_name === undefined || !project ? items :
314
+ await Promise.all(items.map(async item => {
315
+ return await this.get(table_name,
316
+ Object.fromEntries(Object.entries(item).filter((([key_name, key_value]) => table_key_names.includes(key_name))))
317
+ )
318
+ }))
319
+ .then(items => items.filter(item => item !== undefined))
320
+ )
321
+ return items
274
322
  }
275
323
 
276
324
  async query_range(
277
- table : string,
325
+ table_index_name : string,
278
326
  primary_key : Record<string, any>,
279
327
  secondary_key_range : Record<string, (string|number)[]>,
280
328
  options : {
281
- index_name? : string,
282
329
  reverse? : boolean,
283
- compile? : boolean
330
+ compile? : boolean,
331
+ project? : boolean,
332
+ attribute_names? : string[]
284
333
  } = {}
285
334
  ) : Promise<Record<string, any>[]>{
286
- const index_name = options.index_name !== undefined ? options.index_name : undefined
335
+ const table_name : string = table_index_name.split(":")[0]
336
+ const index_name : string | undefined = table_index_name.split(":")[1]
287
337
  const reverse = options.reverse !== undefined ? options.reverse : false
288
338
  const compile = options.compile !== undefined ? options.compile : true
289
- if (Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_range).length !== 1 || Object.values(secondary_key_range)[0].length !== 2) {
339
+ const project = options.compile !== undefined ? options.project : true
340
+ const attribute_names : string[] = options.attribute_names !== undefined ? options.attribute_names : []
341
+
342
+ const table_key_names = await this.get_table_key_names(table_name)
343
+ if (table_key_names === undefined || Object.keys(primary_key).length !== 1 || Object.keys(secondary_key_range).length !== 1 || Object.values(secondary_key_range)[0].length !== 2) {
290
344
  return []
291
345
  }
292
346
  const converted_primary_value = convert_input(Object.values(primary_key)[0])
@@ -295,8 +349,9 @@ export class UtilsDynamoDB {
295
349
  if (converted_primary_value === undefined || converted_secondary_range_start_value === undefined || converted_secondary_range_end_value === undefined) {
296
350
  return []
297
351
  }
352
+ const projection_expression = attribute_names.map(attribute_name => "#" + attribute_name).join(", ")
298
353
  const request : QueryCommandInput = {
299
- TableName : table,
354
+ TableName : table_name,
300
355
  IndexName : index_name,
301
356
  ExpressionAttributeNames: {
302
357
  "#a": Object.keys(primary_key)[0],
@@ -307,17 +362,27 @@ export class UtilsDynamoDB {
307
362
  ":b1": converted_secondary_range_start_value,
308
363
  ":b2": converted_secondary_range_end_value
309
364
  },
365
+ ProjectionExpression : projection_expression.length > 0 ? projection_expression : undefined,
310
366
  KeyConditionExpression: "#a = :a AND (#b BETWEEN :b1 AND :b2)",
311
367
  ScanIndexForward : !reverse
312
368
  }
313
- return await compile_pages(request, (request) => this.dynamodb.query(request), compile)
369
+ const items = await compile_pages(request, (request) => this.dynamodb.query(request), compile)
370
+ .then(async items => index_name === undefined || !project ? items :
371
+ await Promise.all(items.map(async item => {
372
+ return await this.get(table_name,
373
+ Object.fromEntries(Object.entries(item).filter((([key_name, key_value]) => table_key_names.includes(key_name))))
374
+ )
375
+ }))
376
+ .then(items => items.filter(item => item !== undefined))
377
+ )
378
+ return items
314
379
  }
315
380
 
316
- async set(table : string, key : Record<string, any>, attributes : Record<string, any>) : Promise<void> {
381
+ async set(table_name : string, key : Record<string, any>, attributes : Record<string, any>) : Promise<void> {
317
382
  const converted_key = convert_input(key)?.M
318
383
 
319
384
  const request : UpdateItemCommandInput = {
320
- TableName : table,
385
+ TableName : table_name,
321
386
  Key : converted_key,
322
387
  UpdateExpression: "set " + Object.keys(attributes)
323
388
  .filter(attribute_name => !Object.keys(key).includes(attribute_name))
@@ -338,11 +403,11 @@ export class UtilsDynamoDB {
338
403
  await this.dynamodb.updateItem(request)
339
404
  }
340
405
 
341
- async append(table : string, key : Record<string, any>, attributes : Record<string, any[]>) : Promise<void>{
406
+ async append(table_name : string, key : Record<string, any>, attributes : Record<string, any[]>) : Promise<void>{
342
407
  const converted_key = convert_input(key)?.M
343
408
 
344
409
  const request : UpdateItemCommandInput = {
345
- TableName : table,
410
+ TableName : table_name,
346
411
  Key : converted_key,
347
412
  UpdateExpression: "set " + Object.keys(attributes)
348
413
  .filter(attribute_name => attributes[attribute_name] !== undefined)
@@ -360,8 +425,8 @@ export class UtilsDynamoDB {
360
425
  this.dynamodb.updateItem(request)
361
426
  }
362
427
 
363
- async add(table : string, key : Record<string, any>, attributes : Record<string, any[]>) : Promise<void> {
364
- const item = await this.get(table, key, true)
428
+ async add(table_name : string, key : Record<string, any>, attributes : Record<string, any[]>) : Promise<void> {
429
+ const item = await this.get(table_name, key, true)
365
430
  if (item === undefined) {
366
431
  return
367
432
  }
@@ -378,14 +443,14 @@ export class UtilsDynamoDB {
378
443
  if (Object.values(new_attributes).flat().length === 0) {
379
444
  return undefined
380
445
  }
381
- return await this.append(table, key, attributes)
446
+ return await this.append(table_name, key, attributes)
382
447
  }
383
448
 
384
- async remove(table : string, key : Record<string, any>, attributes : string[]) : Promise<void> {
449
+ async remove(table_name : string, key : Record<string, any>, attributes : string[]) : Promise<void> {
385
450
  const converted_key = convert_input(key)?.M
386
451
 
387
452
  const request : UpdateItemCommandInput = {
388
- TableName : table,
453
+ TableName : table_name,
389
454
  Key : converted_key,
390
455
  UpdateExpression: "remove " + attributes
391
456
  .map(attribute_name => "#" + attribute_name).join(", "),
@@ -396,37 +461,37 @@ export class UtilsDynamoDB {
396
461
  await this.dynamodb.updateItem(request)
397
462
  }
398
463
 
399
- async create(table : string, key : Record<string, any>, attributes : Record<string, any> = {}) : Promise<void> {
400
- const item = await this.get(table, key, true)
464
+ async create(table_name : string, key : Record<string, any>, attributes : Record<string, any> = {}) : Promise<void> {
465
+ const item = await this.get(table_name, key, true)
401
466
  if (item !== undefined) {
402
467
  return
403
468
  }
404
469
  const converted_key = convert_input(key)?.M
405
470
  const converted_attributes = convert_input(attributes)?.M
406
471
  await this.dynamodb.putItem({
407
- TableName : table,
472
+ TableName : table_name,
408
473
  Item : { ...converted_key, ...converted_attributes }
409
474
  })
410
475
  }
411
476
 
412
- async delete(table : string, key : Record<string, any>) : Promise<void> {
477
+ async delete(table_name : string, key : Record<string, any>) : Promise<void> {
413
478
  const converted_key = convert_input(key)?.M
414
479
  await this.dynamodb.deleteItem({
415
- TableName: table,
480
+ TableName: table_name,
416
481
  Key: converted_key
417
482
  })
418
483
  }
419
484
 
420
- async duplicate_attribute(table : string, attribute_name : string, new_attribute_name : string) : Promise<void>{
485
+ async duplicate_attribute(table_name : string, attribute_name : string, new_attribute_name : string) : Promise<void>{
421
486
  const table_metadata = await this.dynamodb.describeTable({
422
- TableName : table
487
+ TableName : table_name
423
488
  })
424
489
  if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
425
490
  return
426
491
  }
427
492
  const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
428
493
  .filter(table_key_name => table_key_name !== undefined)
429
- const items = await this.scan(table, { attribute_names : table_key_names.concat([attribute_name, new_attribute_name]) })
494
+ const items = await this.scan(table_name, { attribute_names : table_key_names.concat([attribute_name, new_attribute_name]) })
430
495
  if (items.filter(item => item[new_attribute_name] !== undefined).length > 0) {
431
496
  console.log("Cannot rename.", new_attribute_name, "is an existing item.")
432
497
  return
@@ -436,24 +501,20 @@ export class UtilsDynamoDB {
436
501
  continue
437
502
  }
438
503
  const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]))
439
- await this.set(table, key, { [new_attribute_name] : item[attribute_name] })
504
+ await this.set(table_name, key, { [new_attribute_name] : item[attribute_name] })
440
505
  }
441
506
  }
442
507
 
443
- async remove_attribute(table : string, attribute_name : string) {
444
- const table_metadata = await this.dynamodb.describeTable({
445
- TableName : table
446
- })
447
- if (table_metadata.Table === undefined || table_metadata.Table.KeySchema === undefined) {
448
- return
508
+ async remove_attribute(table_name : string, attribute_name : string) {
509
+ const table_key_names = await this.get_table_key_names(table_name)
510
+ if (table_key_names === undefined) {
511
+ return undefined
449
512
  }
450
- const table_key_names = table_metadata.Table.KeySchema.map(key => key.AttributeName)
451
- .filter(table_key_name => table_key_name !== undefined)
452
- const items = await this.scan(table)
513
+ const items = await this.scan(table_name)
453
514
  .then(items => items.filter(item => item[attribute_name] !== undefined))
454
515
  for (const item of items) {
455
516
  const key = Object.fromEntries(table_key_names.map(key_name => [key_name, item[key_name]]))
456
- await this.remove(table, key, [attribute_name])
517
+ await this.remove(table_name, key, [attribute_name])
457
518
  }
458
519
  }
459
520
  }
package/src/f.ts ADDED
@@ -0,0 +1,25 @@
1
+ import { SecretsManager } from "@aws-sdk/client-secrets-manager"
2
+ import { TriangleUtils } from "."
3
+
4
+ const secret_name = "triage_config"
5
+
6
+ const secrets_manager = new SecretsManager({ region: "us-east-1" })
7
+
8
+ const response = await secrets_manager.getSecretValue({
9
+ SecretId: secret_name
10
+ })
11
+
12
+ const api_keys = JSON.parse(response?.SecretString || "")
13
+
14
+ const config = {
15
+ google_email : "louishou@triangleanalytics.com",
16
+ alerts_email : "alerts@triangleanalytics.com",
17
+ region : "us-east-1",
18
+ ...api_keys
19
+ }
20
+
21
+ const utils = new TriangleUtils(config)
22
+
23
+ const foods = await utils.dynamodb.query("triage_docket_documents:register_document_id", { register_document_id : "E6-17065" })
24
+
25
+ console.log(foods)