apf-node-common 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -24,6 +24,7 @@ const AWSAPIKeyGenerator = require('./utils/aws/AWSAPIKeyGenerator');
24
24
  const AESEncryptionUsingKMS = require('./utils/aws/AESEncryptionUsingKMS');
25
25
  const AccessInterceptor = require('./utils/AccessInterceptor');
26
26
  const AWSSMSUtils = require('./utils/aws/AWSSMSUtils');
27
+ const DynamoDbService = require('./utils/aws/DynamoDbService');
27
28
  const HashIds = require('./utils/HashIds');
28
29
  const modulePath = path.resolve(__dirname, 'node_modules');
29
30
  const TimeZoneConverter = require('./utils/TimeZoneConverter');
@@ -154,7 +155,8 @@ module.exports.AWSUsagePlanAndApiKey = {
154
155
  }
155
156
 
156
157
  module.exports.AccessInterceptor = {
157
- authorize: AccessInterceptor.authorize
158
+ // authorize: AccessInterceptor.authorize,
159
+ authorizeWithOpenApiJson: AccessInterceptor.authorizeWithOpenApiJson
158
160
  }
159
161
 
160
162
  module.exports.HashIds = {
@@ -185,4 +187,13 @@ module.exports.Internationalization = {
185
187
 
186
188
  module.exports.URLShorteningService = {
187
189
  shortenURL: URLShorteningService.shortenURL
190
+ };
191
+
192
+ module.exports.DynamoDbService = {
193
+ scanRecords: DynamoDbService.scanRecords,
194
+ getRecord: DynamoDbService.getRecord,
195
+ queryRecords: DynamoDbService.queryRecords,
196
+ putRecord: DynamoDbService.putRecord,
197
+ updateRecord: DynamoDbService.updateRecord,
198
+ deleteRecord: DynamoDbService.deleteRecord
188
199
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apf-node-common",
3
- "version": "2.0.1",
3
+ "version": "3.0.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -21,9 +21,10 @@
21
21
  "moment": "^2.24.0",
22
22
  "moment-timezone": "^0.5.27",
23
23
  "path-to-regexp": "^6.1.0",
24
- "swagger-jsdoc": "^3.4.0",
25
24
  "winston": "^3.2.1",
26
25
  "winston-aws-cloudwatch": "^3.0.0",
27
- "winston-daily-rotate-file": "^4.5.0"
26
+ "winston-daily-rotate-file": "^4.5.0",
27
+ "@aws-sdk/client-dynamodb": "^3.1021.0",
28
+ "@aws-sdk/lib-dynamodb": "^3.1021.0"
28
29
  }
29
30
  }
@@ -1,28 +1,28 @@
1
- const { pathToRegexp } = require('path-to-regexp')
2
- const swaggerJSDoc = require('swagger-jsdoc');
3
-
4
- function authorize(req, res, options, httpContext, next) {
5
- console.log('AccessInterceptor -> authorize');
6
- /*var options = {
7
- swaggerDefinition: swaggerDefinition,
8
- apis: ['swagger/*.js']
9
- };*/
10
-
11
- const swaggerSpec = swaggerJSDoc(options)
12
- const swaggerJson = JSON.parse(JSON.stringify(swaggerSpec))
13
- try {
14
- const isAllowed = validateAccess(swaggerJson, req, httpContext, res);
15
- if (isAllowed === false) {
16
- return isAllowed
17
- }
18
- return true;
19
-
20
- } catch (error) {
21
- console.log(error);
22
- return error;
23
- }
24
- next();
25
- }
1
+ const { pathToRegexp } = require('path-to-regexp');
2
+ // const swaggerJSDoc = require('swagger-jsdoc');
3
+
4
+ // function authorize(req, res, options, httpContext, next) {
5
+ // console.log('AccessInterceptor -> authorize');
6
+ // /*var options = {
7
+ // swaggerDefinition: swaggerDefinition,
8
+ // apis: ['swagger/*.js']
9
+ // };*/
10
+
11
+ // const swaggerSpec = swaggerJSDoc(options)
12
+ // const swaggerJson = JSON.parse(JSON.stringify(swaggerSpec))
13
+ // try {
14
+ // const isAllowed = validateAccess(swaggerJson, req, httpContext, res);
15
+ // if (isAllowed === false) {
16
+ // return isAllowed
17
+ // }
18
+ // return true;
19
+
20
+ // } catch (error) {
21
+ // console.log(error);
22
+ // return error;
23
+ // }
24
+ // next();
25
+ // }
26
26
 
27
27
  /**
28
28
  * Authorize request based on swagger specification
@@ -111,5 +111,5 @@ function getReplacePath(path) {
111
111
  str = str.replace(/}/g, '');
112
112
  return str
113
113
  }
114
- exports.authorize = authorize;
114
+ // exports.authorize = authorize;
115
115
  exports.authorizeWithOpenApiJson = authorizeWithOpenApiJson;
@@ -0,0 +1,289 @@
1
+ const log = require('apf-node-common/Logger').getLogger();
2
+ const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
3
+
4
+ const { DynamoDBDocumentClient,
5
+ ScanCommand,
6
+ GetCommand,
7
+ PutCommand,
8
+ UpdateCommand,
9
+ DeleteCommand,
10
+ QueryCommand } = require("@aws-sdk/lib-dynamodb");
11
+
12
+ const client = new DynamoDBClient({});
13
+ const dynamoDb = DynamoDBDocumentClient.from(client);
14
+
15
+
16
+ module.exports = {
17
+ // ══════════════════════════════════════════════════════════════════════════
18
+ // SCAN — fetch multiple records with optional filters
19
+ // ══════════════════════════════════════════════════════════════════════════
20
+ /**
21
+ * @param {string} tableName
22
+ * @param {Object} filters - optional e.g. { IsDeleted: false }
23
+ *
24
+ * @example
25
+ * // Fetch where IsDeleted <> true OR IsDeleted missing ✅
26
+ const items = await DynamoDbService.scanRecords(TABLE_NAME, {
27
+ FilterExpression: "(#IsDeleted <> :isDeleted OR attribute_not_exists(#IsDeleted))",
28
+
29
+ const records = await DynamoDbService.scanRecords("AllowedOriginUrls", {
30
+ FilterExpression: "#MerchantId > :minId AND (#IsDeleted <> :isDeleted OR attribute_not_exists(#IsDeleted)) AND #AllowedUrls = :url",
31
+ ExpressionAttributeNames: {
32
+ "#MerchantId": "MerchantId",
33
+ "#IsDeleted": "IsDeleted",
34
+ "#AllowedUrls": "AllowedUrls"
35
+ },
36
+ ExpressionAttributeValues: {
37
+ ":minId": 0,
38
+ ":isDeleted": true,
39
+ ":url": "https://dcy2no2504rgw.cloudfront.net/#/login"
40
+ }
41
+ });
42
+ *
43
+ * // Fetch all records no filter
44
+ * const all = await scanRecords("AllowedOriginUrls");
45
+ */
46
+ async scanRecords(tableName, params = {}) {
47
+ try {
48
+ const command = new ScanCommand({
49
+ TableName: tableName, // ← always set
50
+ ...params // ← caller provides everything else (e.g. FilterExpression, ExpressionAttributeValues)
51
+ });
52
+
53
+ const result = await dynamoDb.send(command);
54
+ log.info(`[DynamoDB] scanRecords(${tableName}) → ${result.Items.length} records`);
55
+ return result.Items;
56
+
57
+ } catch (error) {
58
+ log.error(`[DynamoDB] scanRecords failed on ${tableName}:`, error.message);
59
+ throw error;
60
+ }
61
+ },
62
+
63
+ /**
64
+ * Fetches a single record by primary key from DynamoDB.
65
+ *
66
+ * TABLE: AllowedOriginUrls
67
+ * Attributes: MerchantId (PK), IsDeleted, AllowedUrls
68
+ *
69
+ * EXAMPLES:
70
+ *
71
+ * const record = await DynamoDbService.getRecord("AllowedOriginUrls", { MerchantId: 1277 });
72
+ * // → { MerchantId: 1277, IsDeleted: false, AllowedUrls: "https://shop.com" }
73
+ *
74
+ * // Fetch specific attributes only (ProjectionExpression)
75
+ * const record = await DynamoDbService.getRecord("AllowedOriginUrls", { MerchantId: 1277 }, {
76
+ * ProjectionExpression: "MerchantId, AllowedUrls",
77
+ * });
78
+ * // → { MerchantId: 1277, AllowedUrls: "https://shop.com" } (IsDeleted not returned)
79
+ *
80
+ * // Returns null if record not found
81
+ * const record = await DynamoDbService.getRecord("AllowedOriginUrls", { MerchantId: 9999 });
82
+ * // → null
83
+ */
84
+ async getRecord(tableName, key, params = {}) {
85
+ try {
86
+ const result = await dynamoDb.send(new GetCommand({
87
+ TableName: tableName,
88
+ Key: key,
89
+ ...params // ← caller provides anything extra
90
+ }));
91
+
92
+ log.info(`[DynamoDB] getRecord(${tableName}) → ${result.Item ? "found" : "not found"}`);
93
+ return result.Item || null;
94
+
95
+ } catch (error) {
96
+ log.error(`[DynamoDB] getRecord failed on ${tableName}:`, error.message);
97
+ throw error;
98
+ }
99
+ },
100
+
101
+
102
+ /**
103
+ * Fetches multiple records by partition key — faster than scan.
104
+ * Use when table has sort key and you need multiple records
105
+ * for the same partition key.
106
+ * Pl note QueryCommand REQUIRES KeyConditionExpression which must include the partition key and can optionally include the sort key.
107
+ * KeyConditionExpression only works on
108
+ * Partition Key → only supports = operator
109
+ * Sort Key → supports =, <, >, BETWEEN, begins_with
110
+ *
111
+ * TABLE: AllowedOriginUrls
112
+ * Attributes: MerchantId (PK), IsDeleted, AllowedUrls
113
+ *
114
+ * EXAMPLES:
115
+ *
116
+ * // Fetch all records for a merchant
117
+ * const records = await DynamoDbService.queryRecords("AllowedOriginUrls", {
118
+ * KeyConditionExpression: "#pk = :pkVal",
119
+ * ExpressionAttributeNames: { "#pk": "MerchantId" },
120
+ * ExpressionAttributeValues: { ":pkVal": 1277 }
121
+ * });
122
+ * // → [{ MerchantId: 1277, IsDeleted: false, AllowedUrls: "https://shop.com" }]
123
+ *
124
+ * // Fetch with additional filter
125
+ * const records = await DynamoDbService.queryRecords("AllowedOriginUrls", {
126
+ * KeyConditionExpression: "#pk = :pkVal",
127
+ * FilterExpression: "IsDeleted <> :isDeleted",
128
+ * ExpressionAttributeNames: { "#pk": "MerchantId" },
129
+ * ExpressionAttributeValues: { ":pkVal": 1277, ":isDeleted": true }
130
+ * });
131
+ */
132
+ async queryRecords(tableName, params = {}) {
133
+ try {
134
+ const result = await dynamoDb.send(new QueryCommand({
135
+ TableName: tableName, // ← always set
136
+ ...params // ← caller provides everything else (e.g. KeyConditionExpression, FilterExpression, ExpressionAttributeValues)
137
+ }));
138
+
139
+ log.info(`[DynamoDB] queryRecords(${tableName}) → ${result.Items.length} records`);
140
+ return result.Items;
141
+
142
+ } catch (error) {
143
+ log.error(`[DynamoDB] queryRecords failed on ${tableName}:`, error.message);
144
+ throw error;
145
+ }
146
+ },
147
+
148
+
149
+ /**
150
+ * Inserts or replaces a record in DynamoDB.
151
+ * If record with same key exists → replaces entire record.
152
+ *
153
+ * TABLE: AllowedOriginUrls
154
+ * Attributes: MerchantId (PK), IsDeleted, AllowedUrls
155
+ *
156
+ * EXAMPLES:
157
+ *
158
+ * // Insert new record
159
+ * await DynamoDbService.putRecord("AllowedOriginUrls", {
160
+ * MerchantId: 1277,
161
+ * AllowedUrls: "https://shop.com",
162
+ * IsDeleted: false
163
+ * });
164
+ *
165
+ * // Insert with condition — only if record does NOT already exist
166
+ * await DynamoDbService.putRecord("AllowedOriginUrls", {
167
+ * MerchantId: 1277,
168
+ * AllowedUrls: "https://shop.com",
169
+ * IsDeleted: false
170
+ * }, {
171
+ * ConditionExpression: "attribute_not_exists(MerchantId)"
172
+ * });
173
+ */
174
+ async putRecord(tableName, item, params = {}) {
175
+ try {
176
+ await dynamoDb.send(new PutCommand({
177
+ TableName: tableName,
178
+ Item: item,
179
+ ...params // ← caller provides anything extra (e.g. ConditionExpression, ExpressionAttributeValues for conditions)
180
+ }));
181
+
182
+ log.info(`[DynamoDB] putRecord(${tableName}) → saved successfully`);
183
+ return true;
184
+
185
+ } catch (error) {
186
+ log.error(`[DynamoDB] putRecord failed on ${tableName}:`, error.message);
187
+ throw error;
188
+ }
189
+ },
190
+
191
+
192
+ /**
193
+ * Updates specific fields of an existing record by primary key.
194
+ * Only provided fields are updated — rest remain unchanged.
195
+ *
196
+ * TABLE: AllowedOriginUrls
197
+ * Attributes: MerchantId (PK), IsDeleted, AllowedUrls
198
+ *
199
+ * EXAMPLES:
200
+ *
201
+ * // Soft delete a record
202
+ * await DynamoDbService.updateRecord("AllowedOriginUrls",
203
+ * { MerchantId: 1277 },
204
+ * {
205
+ * UpdateExpression: "SET #IsDeleted = :val",
206
+ * ExpressionAttributeNames: { "#IsDeleted": "IsDeleted" },
207
+ * ExpressionAttributeValues: { ":val": true }
208
+ * }
209
+ * );
210
+ *
211
+ * // Update AllowedUrls
212
+ * await DynamoDbService.updateRecord("AllowedOriginUrls",
213
+ * { MerchantId: 1277 },
214
+ * {
215
+ * UpdateExpression: "SET #AllowedUrls = :url, #IsDeleted = :isDeleted",
216
+ * ExpressionAttributeNames: { "#AllowedUrls": "AllowedUrls", "#IsDeleted": "IsDeleted" },
217
+ * ExpressionAttributeValues: { ":url": "https://newshop.com", ":isDeleted": false }
218
+ * }
219
+ * );
220
+ *
221
+ * // Update only if record exists
222
+ * await DynamoDbService.updateRecord("AllowedOriginUrls",
223
+ * { MerchantId: 1277 },
224
+ * {
225
+ * UpdateExpression: "SET #AllowedUrls = :url",
226
+ * ConditionExpression: "attribute_exists(MerchantId)",
227
+ * ExpressionAttributeNames: { "#AllowedUrls": "AllowedUrls" },
228
+ * ExpressionAttributeValues: { ":url": "https://newshop.com" }
229
+ * }
230
+ * );
231
+ */
232
+ async updateRecord(tableName, key, params = {}) {
233
+ try {
234
+ const result = await dynamoDb.send(new UpdateCommand({
235
+ TableName: tableName,
236
+ Key: key,
237
+ ReturnValues: "ALL_NEW", // ← full updated item
238
+ ...params // ← caller provides UpdateExpression etc
239
+ }));
240
+
241
+ log.info(`[DynamoDB] updateRecord(${tableName}) → updated:`, result.Attributes);
242
+ return result.Attributes;
243
+
244
+ } catch (error) {
245
+ log.error(`[DynamoDB] updateRecord failed on ${tableName}:`, error.message);
246
+ throw error;
247
+ }
248
+ },
249
+
250
+
251
+ /**
252
+ * Deletes a record by primary key from DynamoDB.
253
+ *
254
+ * TABLE: AllowedOriginUrls
255
+ * Attributes: MerchantId (PK), IsDeleted, AllowedUrls
256
+ *
257
+ * EXAMPLES:
258
+ *
259
+ * // Hard delete by partition key
260
+ * await DynamoDbService.deleteRecord("AllowedOriginUrls", { MerchantId: 1277 });
261
+ *
262
+ * // Delete only if IsDeleted is already true (conditional delete)
263
+ * await DynamoDbService.deleteRecord("AllowedOriginUrls",
264
+ * { MerchantId: 1277 },
265
+ * {
266
+ * ConditionExpression: "#IsDeleted = :val",
267
+ * ExpressionAttributeNames: { "#IsDeleted": "IsDeleted" },
268
+ * ExpressionAttributeValues: { ":val": true }
269
+ * }
270
+ * );
271
+ */
272
+ async deleteRecord(tableName, key, params = {}) {
273
+ try {
274
+ await dynamoDb.send(new DeleteCommand({
275
+ TableName: tableName,
276
+ Key: key,
277
+ ...params // ← caller provides anything extra ✅
278
+ }));
279
+
280
+ log.info(`[DynamoDB] deleteRecord(${tableName}) → deleted successfully`);
281
+ return true;
282
+
283
+ } catch (error) {
284
+ log.error(`[DynamoDB] deleteRecord failed on ${tableName}:`, error.message);
285
+ throw error;
286
+ }
287
+ }
288
+
289
+ }