ac-awssecrets 2.4.3 → 2.5.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+
2
+ # [2.5.0](https://github.com/admiralcloud/ac-awssecrets/compare/v2.4.4..v2.5.0) (2025-05-06 18:01:30)
3
+
4
+
5
+ ### Feature
6
+
7
+ * **App:** Allow batch fetching secret parameters | MP | [0d74ae3d45edde466aa36ca15e3f3a689676cc37](https://github.com/admiralcloud/ac-awssecrets/commit/0d74ae3d45edde466aa36ca15e3f3a689676cc37)
8
+ To speed up processes, this version now allows batch fetching of secret parameters
9
+ Related issues:
10
+ ### Tests
11
+
12
+ * **App:** Fixed test | MP | [996dabf3f6287150da5aaa048bcb2c3b7aa9a289](https://github.com/admiralcloud/ac-awssecrets/commit/996dabf3f6287150da5aaa048bcb2c3b7aa9a289)
13
+ Fixed test
14
+ Related issues:
15
+ ### Chores
16
+
17
+ * **App:** Updated packages | MP | [42be94ad3cb396fc40f4300eec4463d6cfe18b3a](https://github.com/admiralcloud/ac-awssecrets/commit/42be94ad3cb396fc40f4300eec4463d6cfe18b3a)
18
+ Updated packages
19
+ Related issues:
20
+
21
+ ## [2.4.4](https://github.com/admiralcloud/ac-awssecrets/compare/v2.4.3..v2.4.4) (2025-04-20 06:17:50)
22
+
23
+
24
+ ### Bug Fix
25
+
26
+ * **App:** Package updates | MP | [fda6b66737639aaaefab76ec6d0cd91240039dd2](https://github.com/admiralcloud/ac-awssecrets/commit/fda6b66737639aaaefab76ec6d0cd91240039dd2)
27
+ Package updates
28
+ Related issues:
1
29
 
2
30
  ## [2.4.3](https://github.com/admiralcloud/ac-awssecrets/compare/v2.4.2..v2.4.3) (2025-04-19 18:41:08)
3
31
 
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager')
2
- const { SSMClient, GetParameterCommand, GetParametersByPathCommand } = require("@aws-sdk/client-ssm")
2
+ const { SSMClient, GetParameterCommand, GetParametersCommand, GetParametersByPathCommand } = require("@aws-sdk/client-ssm")
3
3
 
4
4
  const testConfig = require('./test/config')
5
5
  const functionName = 'ac-awsSecrets'.padEnd(15)
@@ -93,14 +93,127 @@ const awsSecrets = () => {
93
93
 
94
94
  const loadSecretParameters = async({ secretParameters = [], config = {}, testMode = 0, debug = false, throwError = false, region = 'eu-central-1' } = {}) => {
95
95
  const environment = config?.environment || process.env.NODE_ENV || 'development'
96
-
96
+
97
97
  const awsConfig = {
98
98
  region
99
99
  }
100
100
  const ssmClient = new SSMClient(awsConfig)
101
-
102
- const getSecretParameter = async({ name, json = false, array = false, path, property, debug, merge }) => {
103
- const parameterName = `/${environment}/${name}`
101
+
102
+ // Process parameters in batches of 10 (AWS limit for GetParametersCommand)
103
+ const processBatchedParameters = async(paramList) => {
104
+ // Skip if no parameters
105
+ if (paramList.length === 0) return
106
+
107
+ // Split parameters into batches of 10
108
+ const batchSize = 10
109
+ const batches = []
110
+
111
+ for (let i = 0; i < paramList.length; i += batchSize) {
112
+ batches.push(paramList.slice(i, i + batchSize))
113
+ }
114
+
115
+ // Process each batch
116
+ for (const batch of batches) {
117
+ if (testMode === 3) {
118
+ // For test mode, process individually as before
119
+ await Promise.all(batch.map(async param => {
120
+ const parameterName = `/${environment}/${param.name}`
121
+ const found = testConfig.parameterStore.find(item => item.name === parameterName)
122
+ let value = found?.value
123
+
124
+ if (param.json && value) {
125
+ try {
126
+ value = JSON.parse(value)
127
+ }
128
+ catch (e) {
129
+ console.error('%s | %s | %s', functionName, parameterName, e?.message)
130
+ if (throwError) throw e
131
+ }
132
+ }
133
+
134
+ if (debug) {
135
+ console.warn('P %s | T %s | V %j', parameterName, typeof value, value)
136
+ }
137
+ setValue(config, {
138
+ path: (param.path || param.name),
139
+ value,
140
+ array: param.array || false,
141
+ property: param.property,
142
+ merge: param.merge || false
143
+ })
144
+ }))
145
+ }
146
+ else {
147
+ // For production mode, use GetParametersCommand to fetch multiple parameters at once
148
+ try {
149
+ // Get parameter names for this batch
150
+ const parameterNames = batch.map(param => `/${environment}/${param.name}`)
151
+
152
+ // Fetch all parameters in this batch with a single API call
153
+ const command = new GetParametersCommand({
154
+ Names: parameterNames,
155
+ WithDecryption: true
156
+ })
157
+
158
+ const response = await ssmClient.send(command)
159
+ const parameters = response?.Parameters || []
160
+
161
+ // Process each parameter
162
+ await Promise.all(parameters.map(async parameter => {
163
+ // Find corresponding parameter config
164
+ const paramName = parameter.Name
165
+ const paramConfig = batch.find(p => `/${environment}/${p.name}` === paramName)
166
+
167
+ if (!paramConfig) return // Skip if no matching config found
168
+
169
+ let value = parameter.Value
170
+
171
+ if (paramConfig.json && value) {
172
+ try {
173
+ value = JSON.parse(value)
174
+ }
175
+ catch (e) {
176
+ console.error('%s | %s | %s', functionName, paramName, e?.message)
177
+ if (throwError) throw e
178
+ return // Skip this parameter if JSON parsing fails
179
+ }
180
+ }
181
+
182
+ if (debug) {
183
+ console.warn('P %s | T %s | V %j', paramName, typeof value, value)
184
+ }
185
+
186
+ setValue(config, {
187
+ path: (paramConfig.path || paramConfig.name),
188
+ value,
189
+ array: paramConfig.array || false,
190
+ property: paramConfig.property,
191
+ merge: paramConfig.merge || false
192
+ })
193
+ }))
194
+
195
+ // Handle invalid parameters
196
+ if (response?.InvalidParameters?.length > 0) {
197
+ console.error('%s | Invalid parameters: %j', functionName, response.InvalidParameters)
198
+ if (throwError) {
199
+ throw new Error(`Invalid parameters: ${response.InvalidParameters.join(', ')}`)
200
+ }
201
+ }
202
+ }
203
+ catch (e) {
204
+ console.error('%s | Batch parameter fetch error: %s', functionName, e?.message)
205
+ if (throwError) throw e
206
+
207
+ // Fallback: process parameters individually if batch fails
208
+ await Promise.all(batch.map(param => getSecretParameter(param)))
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ // Keep the original getSecretParameter as fallback
215
+ const getSecretParameter = async(param) => {
216
+ const parameterName = `/${environment}/${param.name}`
104
217
  try {
105
218
  let value
106
219
  if (testMode === 3) {
@@ -118,26 +231,25 @@ const awsSecrets = () => {
118
231
  const response = await ssmClient.send(command)
119
232
  value = response?.Parameter?.Value
120
233
  }
121
-
234
+
122
235
  // Extract and return the parameter value
123
- if (json) {
236
+ if (param.json) {
124
237
  value = JSON.parse(value)
125
238
  }
126
-
239
+
127
240
  if (debug) {
128
241
  console.warn('P %s | T %s | V %j', parameterName, typeof value, value)
129
242
  }
130
- setValue(config, { path: (path || name), value, array, property, merge })
131
-
243
+ setValue(config, { path: (param.path || param.name), value, array: param.array, property: param.property, merge: param.merge })
132
244
  }
133
245
  catch (e) {
134
246
  console.error('%s | %s | %s', functionName, parameterName, e?.message)
135
247
  if (throwError) throw e
136
248
  }
137
249
  }
138
-
139
- // pushes multiple paramters into the given path (e.g. params /dev/db/1 and /dev/db/2 (name = /dev/db/*) will be objects in array databases (path))
140
- const getSecretParametersByPath = async({ name, json = false, array = true, path, property, debug, merge }) => {
250
+
251
+ // Keep the original getSecretParametersByPath for wildcard parameters
252
+ const getSecretParametersByPath = async({ path, name, json = false, array, property, merge }) => {
141
253
  if (!path) throw new Error('pathMustBeSet')
142
254
  const parameterName = `/${environment}/${name}`
143
255
  try {
@@ -169,14 +281,14 @@ const awsSecrets = () => {
169
281
  const response = await ssmClient.send(command)
170
282
  valueArray = response?.Parameters
171
283
  }
172
-
284
+
173
285
  for (const item of valueArray) {
174
286
  let value = item?.Value
175
287
  // Extract and return the parameter value
176
288
  if (json) {
177
289
  value = JSON.parse(value)
178
290
  }
179
-
291
+
180
292
  if (debug) {
181
293
  console.warn('P %s | T %s | V %j', item?.Name, typeof value, value)
182
294
  }
@@ -188,13 +300,30 @@ const awsSecrets = () => {
188
300
  if (throwError) throw e
189
301
  }
190
302
  }
191
-
192
- for (const secretParameter of secretParameters) {
193
- if (environment === 'test' && secretParameter?.ignoreInTestMode) continue
194
- if (debug) secretParameter.debug = true
195
- if (secretParameter.name.endsWith('*')) await getSecretParametersByPath(secretParameter)
196
- else await getSecretParameter(secretParameter)
303
+
304
+ // Filter out parameters with ignoreInTestMode = true in test environment
305
+ let filteredParams = secretParameters
306
+ if (environment === 'test') {
307
+ filteredParams = secretParameters.filter(param => !param.ignoreInTestMode)
308
+ }
309
+
310
+ // Add debug if needed
311
+ if (debug) {
312
+ filteredParams.forEach(param => param.debug = true)
197
313
  }
314
+
315
+ // Split parameters into regular and wildcard ones
316
+ const wildcardParams = filteredParams.filter(param => param.name.endsWith('*'))
317
+ const regularParams = filteredParams.filter(param => !param.name.endsWith('*'))
318
+
319
+ // Process parameters in parallel
320
+ await Promise.all([
321
+ // Process regular parameters in batches
322
+ processBatchedParameters(regularParams),
323
+
324
+ // Process wildcard parameters individually (using original method)
325
+ ...wildcardParams.map(param => getSecretParametersByPath(param))
326
+ ])
198
327
  }
199
328
 
200
329
 
package/package.json CHANGED
@@ -3,17 +3,17 @@
3
3
  "author": "Mark Poepping (https://www.admiralcloud.com)",
4
4
  "license": "MIT",
5
5
  "repository": "admiralcloud/ac-awssecrets",
6
- "version": "2.4.3",
6
+ "version": "2.5.0",
7
7
  "dependencies": {
8
- "@aws-sdk/client-secrets-manager": "^3.787.0",
9
- "@aws-sdk/client-ssm": "^3.787.0"
8
+ "@aws-sdk/client-secrets-manager": "^3.803.0",
9
+ "@aws-sdk/client-ssm": "^3.803.0"
10
10
  },
11
11
  "devDependencies": {
12
- "ac-semantic-release": "^0.4.5",
12
+ "ac-semantic-release": "^0.4.6",
13
13
  "c8": "^10.1.3",
14
14
  "chai": "^4.x",
15
15
  "eslint": "9.x",
16
- "mocha": "^11.1.0"
16
+ "mocha": "^11.2.2"
17
17
  },
18
18
  "scripts": {
19
19
  "test": "NODE_ENV=test mocha --reporter spec --bail",
package/test/config.js CHANGED
@@ -36,7 +36,7 @@ const secretParameters = [
36
36
  { name: 'configVar5.path', json: true },
37
37
  { name: 'configVar6', json: true },
38
38
  { name: 'aws', json: true, merge: true },
39
- { name: 'db/*', json: true, merge: true, path: 'db' }
39
+ { name: 'db/*', json: true, merge: true, path: 'db', array: true }
40
40
  ]
41
41
 
42
42
  const parameterStore = [
package/g.sh DELETED
@@ -1,11 +0,0 @@
1
- #!/bin/bash
2
- aws ssm get-parameters-by-path \
3
- --path "/development" \
4
- --recursive \
5
- --profile dev.mfa --region eu-central-1 \
6
- --output json | \
7
- jq '.Parameters[] |
8
- {Name: .Name}'
9
-
10
-
11
- {"secret":"L4w,UU3g;NAh)e6,HV(TTzUZ2G>","name":"acauth","valueHasJSON":"true","defaultApps":{"acapp":"8d09356a-042f-4d4a-9c6f-935329000969","embedlink": "5ffd8ff6-2aeb-44b0-8a6c-f413de58df3c"},"passwordAlgorithm":"aes-256-ctr","password":"8gZVqjzKN@g4XdD7Cq#9qAPh>iPdKNkr"}