@izara_project/izara-core-library-dynamodb 1.0.18 → 1.0.20

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.
Files changed (2) hide show
  1. package/package.json +6 -29
  2. package/src/DynamoDBSharedLib.js +358 -133
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@izara_project/izara-core-library-dynamodb",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Connecting with AWS DynamoDB Resource",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,41 +14,18 @@
14
14
  "license": "AGPL-3.0-or-later",
15
15
  "homepage": "https://bitbucket.org/izara-core-libraries/izara-core-library-dynamodb/src/master/README.md",
16
16
  "devDependencies": {
17
- "jest": "^30.4.1"
17
+ "jest": "^30.4.2"
18
18
  },
19
19
  "jest": {
20
20
  "testEnvironment": "node"
21
21
  },
22
22
  "type": "module",
23
23
  "peerDependencies": {
24
- "@aws-sdk/client-dynamodb": "^3.0.0",
25
- "@aws-sdk/lib-dynamodb": "^3.0.0",
26
- "@aws-sdk/util-dynamodb": "^3.0.0",
27
- "@izara_project/izara-core-library-core": "^1.0.28",
28
- "lodash": "^4.0.0"
29
- },
30
- "peerDependenciesMeta": {
31
- "@aws-sdk/client-dynamodb": {
32
- "optional": false
33
- },
34
- "@aws-sdk/lib-dynamodb": {
35
- "optional": false
36
- },
37
- "@aws-sdk/util-dynamodb": {
38
- "optional": false
39
- },
40
- "@izara_project/izara-core-library-core": {
41
- "optional": false
42
- },
43
- "lodash": {
44
- "optional": true
45
- }
46
- },
47
- "dependencies": {
48
24
  "@aws-sdk/client-dynamodb": "^3.1045.0",
49
25
  "@aws-sdk/lib-dynamodb": "^3.1045.0",
50
26
  "@aws-sdk/util-dynamodb": "^3.996.2",
51
- "@izara_project/izara-core-library-core": "^1.0.32",
52
- "lodash": "^4.18.1"
53
- }
27
+ "@izara_project/izara-core-library-core": "^1.0.28"
28
+ },
29
+ "peerDependenciesMeta": {},
30
+ "dependencies": {}
54
31
  }
@@ -16,11 +16,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
18
  import { DynamoDBDocument } from '@aws-sdk/lib-dynamodb';
19
- import { DynamoDB } from '@aws-sdk/client-dynamodb';
19
+ import { DynamoDB, DescribeTableCommand, DynamoDBClient } from '@aws-sdk/client-dynamodb';
20
20
  import { marshall, unmarshall } from '@aws-sdk/util-dynamodb';
21
21
 
22
- import cloneDeep from 'lodash/cloneDeep';
23
-
24
22
  // External Izara project imports
25
23
  import { NoRetryError } from '@izara_project/izara-core-library-core';
26
24
  // import { getServiceNameWithCache } = require('@izara_project/izara-core-library-service-schemas').serviceConfig;
@@ -35,19 +33,20 @@ const dynamodb = DynamoDBDocument.from(new DynamoDB(), {
35
33
  }
36
34
  });
37
35
 
36
+
38
37
  // Commented imports and alternatives
39
38
  // const izara = require("@izara_project/izara-middleware");
40
39
  // const dynamodb = DynamoDBDocument.from(new DynamoDB());
41
40
  // const dynamodb = require('@izara_project/izara-core-library-external-request').dynamodb
42
41
 
43
42
  /**
44
- * create table name
45
- * @param {Object} _izContext
46
- * @param {string} tableName - suffix tablename
47
- * @param {string} serviceTag
48
- * @returns {string} full tablename
43
+ * Constructs the full DynamoDB table name based on the service tag, environment stage, and table suffix.
44
+ *
45
+ * @param {Object} _izContext - The Izara context object containing logger, correlation IDs, and request details.
46
+ * @param {string} tableName - The base name or suffix of the DynamoDB table.
47
+ * @param {string} [serviceTag=null] - Optional service tag to override the default environment service tag (`process.env.iz_serviceTag`).
48
+ * @returns {string} The fully constructed table name.
49
49
  */
50
-
51
50
  function tableName(_izContext, tableName, serviceTag = null) {
52
51
  if (!serviceTag) {
53
52
  return process.env.iz_serviceTag + process.env.iz_stage + tableName;
@@ -57,10 +56,12 @@ function tableName(_izContext, tableName, serviceTag = null) {
57
56
  }
58
57
 
59
58
  /**
60
- * Creates a string set element for use with documentClient
61
- * @param {string[]} stringSet
59
+ * Creates a DynamoDB String Set from an array of strings.
60
+ * Used for formatting arrays into a Set structure compatible with DynamoDB DocumentClient.
62
61
  *
63
- * @returns {string} String formatted as a string set for Dynamo
62
+ * @param {string[]} stringSetArray - An array of strings to be converted into a Set.
63
+ * @returns {Set<string>} A JavaScript Set containing the string values.
64
+ * @throws {Error} If the Set cannot be created.
64
65
  */
65
66
  function arrayToStringSet(stringSetArray) {
66
67
  try {
@@ -77,19 +78,36 @@ function arrayToStringSet(stringSetArray) {
77
78
  }
78
79
 
79
80
  /**
80
- * Marshalls StringSet from dynamo get/query into an array
81
- * @param {Object} stringSet
81
+ * Converts a DynamoDB StringSet object retrieved from Get or Query operations into a standard JavaScript array.
82
+ * Performs a deep clone to ensure the returned array does not mutate the original object.
82
83
  *
83
- * @returns {string[]}
84
+ * @param {Object} stringSet - The DynamoDB StringSet object containing a `values` property.
85
+ * @param {string[]} stringSet.values - The array of string values inside the DynamoDB StringSet.
86
+ * @returns {string[]} An array of strings extracted from the StringSet.
84
87
  */
85
88
  function stringSetToArray(stringSet) {
86
- return cloneDeep(stringSet.values);
89
+ return structuredClone(stringSet.values);
87
90
  }
88
91
 
92
+ /**
93
+ * Removes undefined values from a given attributes object using DynamoDB's marshall and unmarshall utility.
94
+ *
95
+ * @param {Object} attributes - The attributes object to be processed.
96
+ * @returns {Object} A new object with all undefined values removed, compatible with DynamoDB item structure.
97
+ */
89
98
  function removeUndefined(attributes) {
90
99
  return unmarshall(marshall(attributes, { removeUndefinedValues: true }));
91
100
  }
92
101
 
102
+ /**
103
+ * Extracts and cleans correlation ID values from the Izara context object.
104
+ * Removes any undefined properties before returning.
105
+ *
106
+ * @param {Object} _izContext - The Izara context object.
107
+ * @param {string} _izContext.awsRequestId - The AWS request ID.
108
+ * @param {Object} _izContext.correlationIds - The correlation IDs manager.
109
+ * @returns {Object} An object containing the extracted `awsRequestId` and `xCorrelationId` properties.
110
+ */
93
111
  function correlationIdValues(_izContext) {
94
112
  return removeUndefined({
95
113
  awsRequestId: _izContext.awsRequestId,
@@ -98,6 +116,17 @@ function correlationIdValues(_izContext) {
98
116
  }
99
117
 
100
118
  // ----- Helper function for reformConditionExpression
119
+
120
+ /**
121
+ * Validates and extracts the value of a specific logical side (e.g., left-hand side or right-hand side)
122
+ * for a DynamoDB condition expression. It checks that only one property format is set per side and prepends
123
+ * appropriate prefixes (e.g., `:` for references).
124
+ *
125
+ * @param {Object} logicalElement - The logical element object containing the condition details.
126
+ * @param {string} prefixSide - The prefix identifying the side to check (e.g., 'lhs', 'rhs', 'betweenStart').
127
+ * @returns {string|number} The validated value or reference string for the expression.
128
+ * @throws {NoRetryError} If the side logic is missing, duplicated, or uses an unsupported reference type.
129
+ */
101
130
  function validateSideHand(logicalElement, prefixSide) {
102
131
  // helper function
103
132
  // _izContext.logger.debug('logicalElement in prefixSide', prefixSide);
@@ -146,6 +175,17 @@ function validateSideHand(logicalElement, prefixSide) {
146
175
  } // end validateSideHand
147
176
 
148
177
  // ----- Helper function for query dynamodb
178
+
179
+ /**
180
+ * Helper function to reform query elements into a DynamoDB ConditionExpression and ExpressionAttributeValues.
181
+ * It processes logical elements recursively to build the condition string and maps additional attributes to expression values.
182
+ *
183
+ * @param {Object} _izContext - The Izara context object.
184
+ * @param {Object} queryElements - The elements specifying the query conditions.
185
+ * @param {Object[]} [queryElements.logicalElements] - Array of logical condition definitions.
186
+ * @param {Object} [queryElements.additionalAttributes] - Key-value pairs to be added to ExpressionAttributeValues.
187
+ * @returns {[string|undefined, Object|null]} An array where the first element is the constructed `ConditionExpression` string, and the second is the `ExpressionAttributeValues` object.
188
+ */
149
189
  function reformConditionExpression(_izContext, queryElements) {
150
190
  // _izContext.logger.debug('queryElements', queryElements);
151
191
  // ----- add ConditionExpression to payload if queryElements has prop logicalElements
@@ -176,10 +216,14 @@ function reformConditionExpression(_izContext, queryElements) {
176
216
  }
177
217
 
178
218
  /**
179
- * Process a combination logical elements to conditionalExpression
180
- * @param {object[]} logicalElements
219
+ * Recursively processes an array of logical elements to construct a DynamoDB ConditionalExpression string.
220
+ * Supports logical comparisons (equals, between, etc.), logical operators (and, or), functions (begins_with, attribute_exists), and nested grouping.
181
221
  *
182
- * @returns {string} conditionalExpression
222
+ * @param {Object} _izContext - The Izara context object containing the logger.
223
+ * @param {Object[]} logicalElements - An array of objects defining the logical conditions and operators.
224
+ * @param {number} level - The current recursion depth level to prevent infinite recursion.
225
+ * @returns {string|null} The constructed conditional expression string, or null if the elements array is empty.
226
+ * @throws {NoRetryError|Error} If validation fails, syntax is incorrect, or maximum recursion depth is exceeded.
183
227
  */
184
228
  function processLogicalElements(_izContext, logicalElements, level) {
185
229
  /*
@@ -393,15 +437,17 @@ function processLogicalElements(_izContext, logicalElements, level) {
393
437
  } // module processLogicalElements
394
438
 
395
439
  /**
396
- * Executes a GetItem query to DynamoDB
397
- * @param {string} tableName
398
- * @param {object} keyValues
399
- * @param {object} [queryElements={}] // eg returned only some attributes (ProjectionExpression)
400
- * @param {object} [settings={}]
401
- * @param {boolean} [settings.errorOnNoRecordFound=false]
402
- * @param {boolean} [settings.retryOnErrorNoRecordFound=false]
440
+ * Executes a GetItem operation on DynamoDB to retrieve a single record.
403
441
  *
404
- * @returns {object} single record return value from the query, or Null if none found
442
+ * @param {Object} _izContext - The Izara context object containing the logger and tracing info.
443
+ * @param {string} tableName - The name of the DynamoDB table to read from.
444
+ * @param {Object} keyValues - The primary key (partition key and optional sort key) of the item to retrieve.
445
+ * @param {Object} [queryElements={}] - Additional query options (e.g., ProjectionExpression features). Note: GSI/LSI are not supported for getItem.
446
+ * @param {Object} [settings={}] - Additional behavior settings for the operation.
447
+ * @param {boolean} [settings.errorOnNoRecordFound=false] - If true, throws an error when no item is found instead of returning null.
448
+ * @param {boolean} [settings.retryOnErrorNoRecordFound=false] - If true and errorOnNoRecordFound is also true, throws a retryable Error instead of a NoRetryError.
449
+ * @returns {Promise<Object|null>} A promise that resolves to the retrieved item object, or null if no record is found (and errorOnNoRecordFound is false).
450
+ * @throws {Error|NoRetryError} If the database operation fails or if no record is found and errorOnNoRecordFound is true.
405
451
  */
406
452
  async function getItem(
407
453
  _izContext,
@@ -476,22 +522,42 @@ async function getItem(
476
522
  }
477
523
  } // end module getItem
478
524
 
525
+ // /**
526
+ // * Executes a Query on DynamoDB
527
+ // * @param {string} tableName
528
+ // * @param {object} partitionKeyValue - partitionKey
529
+ // * @param {object} [queryElements={}]
530
+ // * @param {object} queryElements.sortKeyCondition - comparisons and between
531
+ // * @param {string[]} queryElements.projectionExpression - return specific attribute, array of string attribute names.
532
+ // * @param {string} queryElements.select - return specific item eg. ALL_ATTRIBUTES | COUNT
533
+ // * @param {number} queryElements.limit - integer of limit item per pagequery
534
+ // * @param {object} queryElements.exclusiveStartKey - partitionKey and sortKey
535
+ // * @param {string} queryElements.indexName - global secondary index name
536
+ // * @param {string} queryElements.descending - ScanIndexForward be false
537
+ // * @param {object} [settings={}]
538
+ // * @param {numeric} [settings.numPagesToRequest=1] // not sure will use? Script will automatically request multiple pages of results
539
+ // *
540
+ // * @returns {object[], object} array of records from .Items in query result, and queryInfo object with properties such as LastEvaluatedKey
541
+ // */
479
542
  /**
480
- * Executes a Query on DynamoDB
481
- * @param {string} tableName
482
- * @param {object} partitionKeyValue - partitionKey
483
- * @param {object} [queryElements={}]
484
- * @param {object} queryElements.sortKeyCondition - comparisons and between
485
- * @param {string[]} queryElements.projectionExpression - return specific attribute, array of string attribute names.
486
- * @param {string} queryElements.select - return specific item eg. ALL_ATTRIBUTES | COUNT
487
- * @param {number} queryElements.limit - integer of limit item per pagequery
488
- * @param {object} queryElements.exclusiveStartKey - partitionKey and sortKey
489
- * @param {string} queryElements.indexName - global secondary index name
490
- * @param {string} queryElements.descending - ScanIndexForward be false
491
- * @param {object} [settings={}]
492
- * @param {numeric} [settings.numPagesToRequest=1] // not sure will use? Script will automatically request multiple pages of results
543
+ * Executes a Query operation on DynamoDB to retrieve one or multiple records based on partition and sort key conditions.
544
+ * Supports pagination, indexing, and specific attribute projection.
493
545
  *
494
- * @returns {object[], object} array of records from .Items in query result, and queryInfo object with properties such as LastEvaluatedKey
546
+ * @param {Object} _izContext - The Izara context object containing the logger and tracing info.
547
+ * @param {string} tableName - The name of the DynamoDB table to query.
548
+ * @param {Object} partitionKeyValue - An object containing a single key-value pair representing the partition key.
549
+ * @param {Object} [queryElements={}] - Elements to configure the query such as conditions, limits, and projections.
550
+ * @param {Object} [queryElements.sortKeyCondition] - Condition for the sort key, requiring `name`, `comparison`, and `value` (or `betweenStartValue`/`betweenEndValue`).
551
+ * @param {string[]} [queryElements.projectionExpression] - Array of attribute names to return (cannot be used with `select` unless `select` is 'SPECIFIC_ATTRIBUTES').
552
+ * @param {string} [queryElements.select] - Type of data to return (e.g., 'ALL_ATTRIBUTES', 'COUNT', 'SPECIFIC_ATTRIBUTES').
553
+ * @param {number} [queryElements.limit] - Maximum number of items to evaluate per page query.
554
+ * @param {Object} [queryElements.exclusiveStartKey] - Pagination token corresponding to `LastEvaluatedKey` from a previous query.
555
+ * @param {string} [queryElements.indexName] - The name of a Global Secondary Index (GSI) or Local Secondary Index (LSI) to query.
556
+ * @param {boolean} [queryElements.descending] - If true, reverses the sort order (sets `ScanIndexForward` to false).
557
+ * @param {Object} [settings={}] - Additional execution settings.
558
+ * @param {number} [settings.numPagesToRequest=1] - The maximum number of pages to auto-fetch. If > 1, it will paginate automatically until the limit or data ends.
559
+ * @returns {Promise<Object>} A promise that resolves to the DynamoDB query response object, typically containing `Items`, `Count`, `ScannedCount`, and `LastEvaluatedKey`.
560
+ * @throws {NoRetryError|Error} If query configuration is invalid, limits are exceeded, or the database operation fails.
495
561
  */
496
562
  async function query(
497
563
  _izContext,
@@ -792,20 +858,31 @@ async function query(
792
858
  }
793
859
  }
794
860
 
861
+ // /**
862
+ // * Executes a Query on DynamoDB and get only Items
863
+ // * @param {string} tableName
864
+ // * @param {object} partitionKeyValue - partitionKey
865
+ // * @param {object} [queryElements={}]
866
+ // * @param {object} queryElements.sortKeyCondition - comparisons and between
867
+ // * @param {string[]} queryElements.projectionExpression - return specific attribute, array of string attribute names.
868
+ // * @param {string} queryElements.select - return specific item eg. ALL_ATTRIBUTES | COUNT
869
+ // * @param {number} queryElements.limit - integer of limit item per pagequery
870
+ // * @param {object} queryElements.exclusiveStartKey - partitionKey and sortKey
871
+ // * @param {object} [settings={}]
872
+ // * @param {numeric} [settings.numPagesToRequest=1] // not sure will use? Script will automatically request multiple pages of results
873
+ // *
874
+ // * @returns {object[], object} array of records from .Items in query result, and queryInfo object with properties such as LastEvaluatedKey
875
+ // */
795
876
  /**
796
- * Executes a Query on DynamoDB and get only Items
797
- * @param {string} tableName
798
- * @param {object} partitionKeyValue - partitionKey
799
- * @param {object} [queryElements={}]
800
- * @param {object} queryElements.sortKeyCondition - comparisons and between
801
- * @param {string[]} queryElements.projectionExpression - return specific attribute, array of string attribute names.
802
- * @param {string} queryElements.select - return specific item eg. ALL_ATTRIBUTES | COUNT
803
- * @param {number} queryElements.limit - integer of limit item per pagequery
804
- * @param {object} queryElements.exclusiveStartKey - partitionKey and sortKey
805
- * @param {object} [settings={}]
806
- * @param {numeric} [settings.numPagesToRequest=1] // not sure will use? Script will automatically request multiple pages of results
877
+ * Executes a Query operation on DynamoDB and returns only the array of `Items`.
878
+ * Acts as a wrapper around the `query` function for convenience when pagination metadata isn't needed.
807
879
  *
808
- * @returns {object[], object} array of records from .Items in query result, and queryInfo object with properties such as LastEvaluatedKey
880
+ * @param {Object} _izContext - The Izara context object containing the logger.
881
+ * @param {string} tableName - The name of the DynamoDB table to query.
882
+ * @param {Object} partitionKeyValue - An object containing a single key-value pair for the partition key.
883
+ * @param {Object} [queryElements={}] - Elements to configure the query (conditions, limits, projections).
884
+ * @param {Object} [settings={}] - Additional execution settings (e.g., auto-pagination `numPagesToRequest`).
885
+ * @returns {Promise<Object[]>} A promise that resolves to an array of the retrieved item objects.
809
886
  */
810
887
  async function queryResults(
811
888
  _izContext,
@@ -824,25 +901,45 @@ async function queryResults(
824
901
  return queryResults.Items;
825
902
  }
826
903
 
904
+ // /**
905
+ // * Executes a Scan operation on DynamoDB
906
+ // * Scans the entire table or a secondary index with optional filters.
907
+ // * NOTE: Scan operations are expensive and should be used sparingly.
908
+ // *
909
+ // * @param {object} _izContext - Izara context object for logging
910
+ // * @param {string} tableName - Name of the DynamoDB table
911
+ // * @param {object} [queryElements={}] - Optional query parameters
912
+ // * @param {object[]} queryElements.logicalElements - Filter conditions (uses FilterExpression)
913
+ // * @param {object} queryElements.additionalAttributes - Additional attribute values for filters
914
+ // * @param {string[]} queryElements.projectionExpression - Array of attribute names to return
915
+ // * @param {string} queryElements.select - Return mode: ALL_ATTRIBUTES | COUNT | SPECIFIC_ATTRIBUTES
916
+ // * @param {number} queryElements.limit - Maximum items per page
917
+ // * @param {object} queryElements.exclusiveStartKey - Pagination start key
918
+ // * @param {string} queryElements.indexName - Global secondary index name
919
+ // * @param {object} [settings={}] - Operation settings
920
+ // * @param {number} [settings.numPagesToRequest=1] - Number of pages to auto-paginate (default: 1)
921
+ // *
922
+ // * @returns {Promise<object>} Object containing Items, Count, ScannedCount, and LastEvaluatedKey
923
+ // */
827
924
  /**
828
- * Executes a Scan operation on DynamoDB
829
- * Scans the entire table or a secondary index with optional filters.
830
- * NOTE: Scan operations are expensive and should be used sparingly.
831
- *
832
- * @param {object} _izContext - Izara context object for logging
833
- * @param {string} tableName - Name of the DynamoDB table
834
- * @param {object} [queryElements={}] - Optional query parameters
835
- * @param {object[]} queryElements.logicalElements - Filter conditions (uses FilterExpression)
836
- * @param {object} queryElements.additionalAttributes - Additional attribute values for filters
837
- * @param {string[]} queryElements.projectionExpression - Array of attribute names to return
838
- * @param {string} queryElements.select - Return mode: ALL_ATTRIBUTES | COUNT | SPECIFIC_ATTRIBUTES
839
- * @param {number} queryElements.limit - Maximum items per page
840
- * @param {object} queryElements.exclusiveStartKey - Pagination start key
841
- * @param {string} queryElements.indexName - Global secondary index name
842
- * @param {object} [settings={}] - Operation settings
843
- * @param {number} [settings.numPagesToRequest=1] - Number of pages to auto-paginate (default: 1)
925
+ * Executes a Scan operation on DynamoDB to evaluate all items in a table or secondary index.
926
+ * Allows applying optional filters using `logicalElements`.
927
+ * NOTE: Scan operations are computationally expensive and should be used sparingly.
844
928
  *
845
- * @returns {Promise<object>} Object containing Items, Count, ScannedCount, and LastEvaluatedKey
929
+ * @param {Object} _izContext - The Izara context object containing the logger.
930
+ * @param {string} tableName - The name of the DynamoDB table to scan.
931
+ * @param {Object} [queryElements={}] - Optional query parameters for filtering, limiting, and projecting.
932
+ * @param {Object[]} [queryElements.logicalElements] - Filter conditions mapped to `FilterExpression`.
933
+ * @param {Object} [queryElements.additionalAttributes] - Additional attribute values required for the filter conditions.
934
+ * @param {string[]} [queryElements.projectionExpression] - Array of specific attribute names to return.
935
+ * @param {string} [queryElements.select] - Type of data to return (e.g., 'ALL_ATTRIBUTES', 'COUNT').
936
+ * @param {number} [queryElements.limit] - Maximum number of items to evaluate per page.
937
+ * @param {Object} [queryElements.exclusiveStartKey] - Pagination token (`LastEvaluatedKey`) to resume scanning.
938
+ * @param {string} [queryElements.indexName] - Name of a Global Secondary Index (GSI) to scan.
939
+ * @param {Object} [settings={}] - Additional operation settings.
940
+ * @param {number} [settings.numPagesToRequest=1] - Number of pages to auto-paginate before returning.
941
+ * @returns {Promise<Object>} A promise resolving to an object containing `Items`, `Count`, `ScannedCount`, and `LastEvaluatedKey`.
942
+ * @throws {NoRetryError|Error} If configuration is invalid or execution fails.
846
943
  */
847
944
  async function scan(_izContext, tableName, queryElements = {}, settings = {}) {
848
945
  try {
@@ -1008,14 +1105,14 @@ async function scan(_izContext, tableName, queryElements = {}, settings = {}) {
1008
1105
  }
1009
1106
 
1010
1107
  /**
1011
- * Executes a Scan on DynamoDB and returns only Items
1012
- * @param {object} _izContext - Izara context object
1013
- * @param {string} tableName - Name of the DynamoDB table
1014
- * @param {object} [queryElements={}] - Optional query parameters
1015
- * @param {object} [settings={}] - Operation settings
1016
- * @param {number} [settings.numPagesToRequest=1] - Number of pages to request
1108
+ * Executes a Scan operation on DynamoDB and returns only the array of `Items`.
1109
+ * Acts as a wrapper around the `scan` function for convenience when pagination metadata isn't needed.
1017
1110
  *
1018
- * @returns {Promise<object[]>} Array of items from scan result
1111
+ * @param {Object} _izContext - The Izara context object containing the logger.
1112
+ * @param {string} tableName - The name of the DynamoDB table to scan.
1113
+ * @param {Object} [queryElements={}] - Optional parameters for filtering and projection.
1114
+ * @param {Object} [settings={}] - Operation settings, including auto-pagination rules.
1115
+ * @returns {Promise<Object[]>} A promise resolving to an array of the retrieved item objects.
1019
1116
  */
1020
1117
  async function scanResults(
1021
1118
  _izContext,
@@ -1033,22 +1130,24 @@ async function scanResults(
1033
1130
  }
1034
1131
 
1035
1132
  /**
1036
- * Executes a PutItem query on DynamoDB
1037
- * @param {string} tableName
1038
- * @param {object} attributes
1039
- * @param {object} [queryElements={}]
1040
- * @param {object} queryElements.additionalAttributes //attributes needed for the queery but not saved to the Item, eg used in conditional expressions
1041
- * @param {object[]} queryElements.logicalElements
1042
- * @param {string} queryElements.returnValues
1043
- * @param {object} [settings={}]
1044
- * @param {boolean} settings.errorOnConditionalExpNotPass=false] // if set then an error will be thrown, otherwise function returns with no error
1045
- * @param {boolean} [settings.retryOnErrorConditionalExpNotPass=false]
1046
- * @param {boolean} [settings.complexAttributes=false] // default FALSE
1047
- * @param {boolean} [settings.returnQuery=false] // default FALSE
1133
+ * Executes a PutItem operation on DynamoDB to create or replace an item.
1134
+ * Supports basic and complex attribute formatting, string sets, and conditional expressions.
1048
1135
  *
1049
- * @returns {} .. maybe nothing
1136
+ * @param {Object} _izContext - The Izara context object containing the logger.
1137
+ * @param {string} tableName - The name of the DynamoDB table.
1138
+ * @param {Object|Array} attributes - The item attributes to store. If `settings.complexAttributes` is true, this must be an array of objects describing actions and values.
1139
+ * @param {Object} [queryElements={}] - Additional query options (e.g., conditional expressions).
1140
+ * @param {Object} [queryElements.additionalAttributes] - Attributes needed for the query but not saved to the Item (used in conditional expressions).
1141
+ * @param {Object[]} [queryElements.logicalElements] - Array of logical elements mapped to `ConditionExpression`.
1142
+ * @param {string} [queryElements.returnValues] - Values to return (e.g., 'NONE', 'ALL_OLD').
1143
+ * @param {Object} [settings={}] - Additional behavior settings for the operation.
1144
+ * @param {boolean} [settings.errorOnConditionalExpNotPass=false] - If true, throws an error when a conditional expression fails.
1145
+ * @param {boolean} [settings.retryOnErrorConditionalExpNotPass=false] - If true and a condition fails, throws a retryable Error instead of NoRetryError.
1146
+ * @param {boolean} [settings.complexAttributes=false] - If true, processes `attributes` as an array of complex directives (e.g., handles string sets automatically).
1147
+ * @param {boolean} [settings.returnQuery=false] - If true, returns the payload immediately without sending it to DynamoDB.
1148
+ * @returns {Promise<Object|void>} A promise resolving to the return values (if `ReturnValues` != 'NONE'), the payload (if `returnQuery` is true), or void.
1149
+ * @throws {NoRetryError|Error} If attributes are invalid, a conditional expression fails, or a database error occurs.
1050
1150
  */
1051
-
1052
1151
  async function putItem(
1053
1152
  _izContext,
1054
1153
  tableName,
@@ -1677,18 +1776,22 @@ async function updateItem(
1677
1776
  }
1678
1777
 
1679
1778
  /**
1680
- * Executes a DeleteItem query on DynamoDB
1681
- * @param {string} tableName
1682
- * @param {object} keyValues
1683
- * @param {[object]} queryElements
1684
- * @param {object} [queryElements.additionalAttributes]
1685
- * @param {object} [queryElements.logicalElements]
1686
- * @param {object} [queryElements.returnValues]
1687
- * @param {object} settings
1688
- * @param {numeric} [settings.errorOnConditionalExpNotPass=false] // if set then an error will be thrown, otherwise function returns with no error
1689
- * @param {numeric} [settings.retryOnErrorConditionalExpNotPass=false]
1779
+ * Executes a DeleteItem operation on DynamoDB.
1780
+ * Supports conditional deletions and returning the old item's attributes.
1690
1781
  *
1691
- * @returns {} .. maybe nothing
1782
+ * @param {Object} _izContext - The Izara context object containing the logger.
1783
+ * @param {string} tableName - The name of the DynamoDB table.
1784
+ * @param {Object} keyValues - The primary key (partition key and optional sort key) of the item to delete.
1785
+ * @param {Object} [queryElements={}] - Additional query options (e.g., conditional expressions).
1786
+ * @param {Object} [queryElements.additionalAttributes] - Attributes needed for the conditional expression but not saved to the Item.
1787
+ * @param {Object[]} [queryElements.logicalElements] - Array of logical elements mapped to `ConditionExpression`.
1788
+ * @param {string} [queryElements.returnValues] - Values to return (e.g., 'NONE', 'ALL_OLD').
1789
+ * @param {Object} [settings={}] - Additional behavior settings.
1790
+ * @param {boolean} [settings.errorOnConditionalExpNotPass=false] - If true, throws an error when a conditional expression fails.
1791
+ * @param {boolean} [settings.retryOnErrorConditionalExpNotPass=false] - If true and a condition fails, throws a retryable Error instead of NoRetryError.
1792
+ * @param {boolean} [settings.returnQuery=false] - If true, returns the payload immediately without sending it to DynamoDB.
1793
+ * @returns {Promise<Object|void>} A promise resolving to the return values (if `ReturnValues` != 'NONE'), the payload (if `returnQuery` is true), or void.
1794
+ * @throws {NoRetryError|Error} If a conditional expression fails, or a database error occurs.
1692
1795
  */
1693
1796
  async function deleteItem(
1694
1797
  _izContext,
@@ -1777,6 +1880,17 @@ async function deleteItem(
1777
1880
 
1778
1881
  // ----------- helper function -----------
1779
1882
 
1883
+ /**
1884
+ * Helper function to generate logical elements and additional attributes for a ConditionExpression
1885
+ * that checks if a string set exists and matches the old record's string set.
1886
+ *
1887
+ * @param {Object} _izContext - The Izara context object containing the logger.
1888
+ * @param {Object} oldRecord - The existing DynamoDB item used to compare against.
1889
+ * @param {string} stringSetAttName - The attribute name of the string set.
1890
+ * @param {Object} [additionalAttributes={}] - Existing additional attributes object to append to.
1891
+ * @param {Object[]} [logicalElements=[]] - Existing logical elements array to append to.
1892
+ * @returns {[Object, Object[]]} An array containing the updated `additionalAttributes` and `logicalElements`.
1893
+ */
1780
1894
  function QueryElementConditionalCheckStringSetEquals(
1781
1895
  _izContext,
1782
1896
  oldRecord, // filterMain
@@ -1838,7 +1952,15 @@ function QueryElementConditionalCheckStringSetEquals(
1838
1952
  } // end QueryElementConditionalCheckStringSetEquals
1839
1953
 
1840
1954
  //---- Helper fucntion -----
1841
- // module.exports.generateExistLogicalElements = (keyValues) => {
1955
+
1956
+ /**
1957
+ * Helper function to generate an array of logical elements asserting that specified attributes
1958
+ * either exist or do not exist in the DynamoDB table.
1959
+ *
1960
+ * @param {Object} attributes - An object containing the attribute keys to check.
1961
+ * @param {boolean} [existed=false] - If true, asserts `attribute_exists`; otherwise asserts `attribute_not_exists`.
1962
+ * @returns {Object[]} An array of logical elements combined with 'AND' operators.
1963
+ */
1842
1964
  function generateNotExistLogicalElements(attributes, existed = false) {
1843
1965
  if (!existed) {
1844
1966
  existed = 'attribute_not_exists';
@@ -1864,6 +1986,14 @@ function generateNotExistLogicalElements(attributes, existed = false) {
1864
1986
  return logicalElements;
1865
1987
  }
1866
1988
 
1989
+ /**
1990
+ * Helper function to refactor simple key-value attributes into an array of complex attribute directives
1991
+ * (specifically enforcing the 'SET' action) for `updateItem` or `putItem`.
1992
+ *
1993
+ * @param {Object} attributes - A simple key-value map of attributes to update.
1994
+ * @param {boolean} [forPutQuery=false] - If true, refrains from enforcing the `action: 'set'` property required by updates.
1995
+ * @returns {Object[]} An array of attribute directive objects.
1996
+ */
1867
1997
  function refractorAttributesToComplex(attributes, forPutQuery = false) {
1868
1998
  // only for SET update
1869
1999
  let returnAttributes = new Array();
@@ -2006,7 +2136,6 @@ async function transactWriteItems(_izContext, writeItems) {
2006
2136
  }
2007
2137
 
2008
2138
  // this.transactWriteItems()
2009
-
2010
2139
  /* NOTE for batchWriteItems cannot perform multiple operations. you cannot put and delete the same item in the same BatchWriteItem request.
2011
2140
  Item not exceed more than 25 requests in the batch, and not batch exceeds 400 KB.The total request size exceeds 16 MB
2012
2141
  action: PutItem | DeleteItem
@@ -2014,6 +2143,19 @@ async function transactWriteItems(_izContext, writeItems) {
2014
2143
  REF: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#batchWriteItem-property
2015
2144
  */
2016
2145
 
2146
+ /**
2147
+ * Executes a batch of delete operations on DynamoDB, automatically chunking requests
2148
+ * into groups of 25 (the maximum allowed by AWS DynamoDB batchWriteItem).
2149
+ *
2150
+ * @param {Object} _izContext - The Izara context object containing the logger and tracing info.
2151
+ * @param {string} tableName - The target DynamoDB table name.
2152
+ * @param {Object[]} writeItems - An array of items to delete.
2153
+ * @param {Object} keyFieldName - Configuration specifying the partition and sort key names.
2154
+ * @param {string} keyFieldName.partitionKeyFieldName - The partition key property name in `writeItems`.
2155
+ * @param {string} [keyFieldName.sortKeyFieldName] - The sort key property name in `writeItems`.
2156
+ * @returns {Promise<void>} A promise that resolves when all batch operations complete successfully.
2157
+ * @throws {Error|string} If the batch deletion process fails.
2158
+ */
2017
2159
  async function batchDeleteItems(
2018
2160
  _izContext,
2019
2161
  tableName,
@@ -2021,39 +2163,44 @@ async function batchDeleteItems(
2021
2163
  keyFieldName
2022
2164
  ) {
2023
2165
  try {
2024
- let size = 25; // maybe should check size < 400 KB
2166
+ const size = 25;
2025
2167
  let chunked = [];
2026
-
2027
2168
  for (let i = 0; i < writeItems.length; i += size) {
2028
2169
  chunked.push(writeItems.slice(i, i + size));
2029
2170
  }
2030
2171
 
2031
- // ---- perform each chuck < 25 ----
2032
2172
  for (let chunkIdx = 0; chunkIdx < chunked.length; chunkIdx++) {
2033
2173
  let chunkSet = chunked[chunkIdx];
2034
- let requestItems = new Array();
2035
- // console.log('chunkSet', chunkSet);
2036
- // check sortKeyFieldName
2174
+ let requestItems = [];
2175
+
2037
2176
  for (let i = 0; i < chunkSet.length; i++) {
2177
+ const item = chunkSet[i];
2178
+ const pKeyName = keyFieldName.partitionKeyFieldName;
2179
+ const sKeyName = keyFieldName.sortKeyFieldName;
2180
+
2181
+ // Construct the primary key object
2182
+ let dynamoKey = {
2183
+ [pKeyName]: item[pKeyName]
2184
+ };
2185
+
2186
+ // 💡 FIX: Only add Sort Key if it exists in the table schema
2187
+ if (sKeyName) {
2188
+ dynamoKey[sKeyName] = item[sKeyName];
2189
+ }
2190
+
2038
2191
  requestItems.push({
2039
2192
  DeleteRequest: {
2040
- Key: {
2041
- [keyFieldName.partitionKeyFieldName]:
2042
- chunkSet[i][keyFieldName.partitionKeyFieldName],
2043
- [keyFieldName.sortKeyFieldName]:
2044
- chunkSet[i][keyFieldName.sortKeyFieldName]
2045
- }
2193
+ Key: dynamoKey
2046
2194
  }
2047
2195
  });
2048
2196
  }
2049
2197
 
2050
2198
  let payload = {
2051
- RequestItems: {
2052
- [tableName]: requestItems
2053
- },
2199
+ RequestItems: { [tableName]: requestItems },
2054
2200
  ReturnConsumedCapacity: 'TOTAL'
2055
2201
  };
2056
- _izContext.logger.debug('payload writeItems... ', payload);
2202
+
2203
+ _izContext.logger.debug('Final Payload for BatchWrite: ', JSON.stringify(payload, null, 2));
2057
2204
 
2058
2205
  const returnValue = await dynamodb.batchWrite(payload);
2059
2206
  _izContext.logger.debug(
@@ -2073,9 +2220,44 @@ async function batchDeleteItems(
2073
2220
  _izContext.logger.info('----- finish all batchDeleteItems -----');
2074
2221
  }
2075
2222
 
2076
- /*https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#batchWriteItem-property
2077
- NOTE: Cannot use complex settings inside PutRequest
2078
- */
2223
+ /**
2224
+ * Executes a batch of put operations to DynamoDB, automatically chunking requests
2225
+ * into groups of 25 (the maximum allowed by AWS DynamoDB batchWriteItem).
2226
+ *
2227
+ * @see https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#batchWriteItem-property
2228
+ *
2229
+ * @description
2230
+ * **Note:** Cannot use complex settings inside PutRequest.
2231
+ *
2232
+ * After completing the DynamoDB query, this function collects capacity units via
2233
+ * `captureCapacityUsed` and stores them in `_izContext.resourceUse`.
2234
+ * Middleware will capture this and send it to the ResourceUse Service later.
2235
+ *
2236
+ * @param {Object} _izContext - The application context object, containing logger and resource tracking.
2237
+ * @param {string} tableNameData - The target DynamoDB table name (or data used to resolve it).
2238
+ * @param {Object[]} writeItems - An array of item objects to be written to the database.
2239
+ * @param {Object} writeItems[].attributes - The core attributes/fields to insert into DynamoDB.
2240
+ * @param {Object} [writeItems[].queryElements] - Optional query elements for validation or processing.
2241
+ * @param {Object} [writeItems[].settings] - Optional item-specific settings (complex settings will be ignored).
2242
+ * @param {Object} [settings] - Settings for the overall batchWriteItem command (e.g., ReturnConsumedCapacity).
2243
+ *
2244
+ * @throws {Error} Throws an error if the batch put process fails.
2245
+ * @returns {Promise<void>} Resolves when all items are successfully written to DynamoDB.
2246
+ *
2247
+ * @example
2248
+ * const items = [
2249
+ * {
2250
+ * attributes: {
2251
+ * configKey: 'configKey_1',
2252
+ * configTag: 'configTag_2',
2253
+ * configValue: 'configValue_3'
2254
+ * },
2255
+ * queryElements: {},
2256
+ * settings: {}
2257
+ * }
2258
+ * ];
2259
+ * await batchPutItems(_izContext, 'MyTable', items, { ReturnConsumedCapacity: 'TOTAL' });
2260
+ */
2079
2261
  async function batchPutItems(
2080
2262
  _izContext,
2081
2263
  tableNameData,
@@ -2154,20 +2336,21 @@ async function batchPutItems(
2154
2336
  );
2155
2337
  } // end loop
2156
2338
  } catch (err) {
2157
- throw ('Error:baatchPutItems', err);
2339
+ throw ('Error:batchPutItems', err);
2158
2340
  }
2159
2341
  }
2160
2342
 
2161
2343
  /**
2344
+ * Captures and accumulates DynamoDB consumed capacity metrics after a query operation.
2345
+ * The collected metrics are stored in `_izContext.resourceUse` for later reporting by middleware.
2346
+ * Supports both single objects and arrays of capacity records (e.g., from `transactWrite`).
2162
2347
  *
2163
- * @param {object} _izContext
2164
- * @param {object | array} capacityUsed normally is object but when call transectionWrite will return array of object
2165
- * @param {string} capacityStatus status of query , read | write
2166
- * @param {string} queryOperation can be, get | query | delete | put | transectionWrite
2167
- *
2168
- * Call this function when finish query dynamodb
2169
- * This function will collect capacity units and store in _izContext.resourceUse
2170
- * Middleware will capture resourceUse and send to ResourceUse Service later
2348
+ * @param {Object} _izContext - The Izara context object containing the resource tracker (`resourceUse`).
2349
+ * @param {Object|Object[]} capacityUsed - The `ConsumedCapacity` object or array of objects returned by DynamoDB.
2350
+ * @param {string} capacityStatus - The category of the operation capacity ('read' or 'write').
2351
+ * @param {string} queryOperation - The specific DynamoDB operation performed (e.g., 'get', 'query', 'delete', 'put', 'transactionWrite', 'scan').
2352
+ * @returns {Promise<void>} Resolves when the capacity tracking completes.
2353
+ * @throws {NoRetryError} If `capacityStatus` is not 'read' or 'write'.
2171
2354
  */
2172
2355
  async function captureCapacityUsed(
2173
2356
  _izContext,
@@ -2215,6 +2398,47 @@ async function captureCapacityUsed(
2215
2398
  }
2216
2399
  }
2217
2400
 
2401
+ /**
2402
+ * helper function to get partitionKeys and sortKeys for table
2403
+ * @param {*} _izContext
2404
+ * @param {*} tableName
2405
+ * @returns
2406
+ */
2407
+ async function describeTable(
2408
+ _izContext,
2409
+ payload
2410
+ ) {
2411
+ const region = process.env.iz_region;
2412
+ if (!region) {
2413
+ throw new Error("Environment variable 'iz_region' is missing or undefined.");
2414
+ }
2415
+
2416
+ const dynamoDbClient = new DynamoDBClient({ region });
2417
+
2418
+ if (!payload || !payload.TableName) {
2419
+ throw new Error("Missing required parameter: TableName");
2420
+ }
2421
+
2422
+ try {
2423
+ const result = await dynamoDbClient.send(
2424
+ new DescribeTableCommand({ TableName: payload.TableName })
2425
+ );
2426
+
2427
+ const partitionKey = result.Table.KeySchema.find(k => k.KeyType === 'HASH');
2428
+ const sortKey = result.Table.KeySchema.find(k => k.KeyType === 'RANGE');
2429
+
2430
+ // Return a clean object with field names
2431
+ return {
2432
+ partitionKeyFieldName: partitionKey?.AttributeName,
2433
+ sortKeyFieldName: sortKey?.AttributeName // Might be undefined if table has no Sort Key
2434
+ };
2435
+
2436
+ } catch (error) {
2437
+ console.error(`[describeTable Error] Failed for table: ${payload.TableName}`, error);
2438
+ throw error;
2439
+ }
2440
+ }
2441
+
2218
2442
  // Consolidated exports
2219
2443
  export {
2220
2444
  // Table name functions
@@ -2245,5 +2469,6 @@ export {
2245
2469
  // Helper functions
2246
2470
  QueryElementConditionalCheckStringSetEquals,
2247
2471
  generateNotExistLogicalElements,
2248
- refractorAttributesToComplex
2472
+ refractorAttributesToComplex,
2473
+ describeTable
2249
2474
  };