@creator.co/wapi 1.8.8 → 1.9.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/package.json CHANGED
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "@creator.co/wapi",
3
- "version": "1.8.8",
3
+ "version": "1.9.1",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "type": "module",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org/"
11
+ },
8
12
  "scripts": {
9
13
  "build": "task build",
10
14
  "clean": "task clean",
@@ -19,14 +23,14 @@
19
23
  "author": "",
20
24
  "license": "ISC",
21
25
  "dependencies": {
22
- "@aws-sdk/client-dynamodb": "^3.651.1",
23
- "@aws-sdk/client-kms": "^3.651.1",
24
- "@aws-sdk/client-secrets-manager": "^3.651.1",
25
- "@aws-sdk/client-sesv2": "^3.651.1",
26
- "@aws-sdk/client-sns": "^3.651.1",
27
- "@aws-sdk/client-ssm": "^3.651.1",
28
- "@aws-sdk/credential-provider-node": "^3.651.1",
29
- "@aws-sdk/util-dynamodb": "^3.651.1",
26
+ "@aws-sdk/client-dynamodb": "^3.730.0",
27
+ "@aws-sdk/client-kms": "^3.730.0",
28
+ "@aws-sdk/client-secrets-manager": "^3.730.0",
29
+ "@aws-sdk/client-sesv2": "^3.730.0",
30
+ "@aws-sdk/client-sns": "^3.730.0",
31
+ "@aws-sdk/client-ssm": "^3.730.0",
32
+ "@aws-sdk/credential-provider-node": "^3.730.0",
33
+ "@aws-sdk/util-dynamodb": "^3.730.0",
30
34
  "@smithy/node-http-handler": "^3.2.2",
31
35
  "@types/email-templates": "^10.0.4",
32
36
  "@types/nodemailer": "^6.4.10",
@@ -42,12 +46,12 @@
42
46
  "jsonwebtoken": "^9.0.2",
43
47
  "knex": "^3.0.1",
44
48
  "knex-stringcase": "^1.4.6",
45
- "kysely": "^0.27.4",
49
+ "kysely": "^0.28.14",
46
50
  "node-cache": "^5.1.2",
47
- "nodemailer": "^7.0.12",
51
+ "nodemailer": "^8.0.4",
48
52
  "object-hash": "^3.0.0",
49
53
  "parse-duration": "^2.1.3",
50
- "path-to-regexp": "^8.1.0",
54
+ "path-to-regexp": "^8.4.0",
51
55
  "pg": "^8.11.3",
52
56
  "rate-limit-redis": "^4.2.0",
53
57
  "redis": "^4.7.0",
@@ -11,6 +11,31 @@ import Utils from '../Util/Utils.js'
11
11
  * @template QueryParamsType - The type of the query parameters for the request.
12
12
  */
13
13
  export default class Request<InputType, PathParamsType, QueryParamsType> {
14
+ /**
15
+ * Default paths to exclude from verbose logging (health checks, monitoring, etc.)
16
+ */
17
+ private static readonly DEFAULT_NO_LOG_PATHS = [
18
+ '/health',
19
+ '/healthcheck',
20
+ '/health-check',
21
+ '/ping',
22
+ '/status',
23
+ '/ready',
24
+ '/readiness',
25
+ '/liveness',
26
+ '/metrics',
27
+ '/_health',
28
+ '/',
29
+ ]
30
+
31
+ /**
32
+ * Combined list of ignored paths, computed once at class-load time.
33
+ */
34
+ private static readonly IGNORED_PATHS: string[] = [
35
+ ...Request.DEFAULT_NO_LOG_PATHS,
36
+ ...(process.env.NO_LOG_PATHS ? process.env.NO_LOG_PATHS.split(',').map(p => p.trim()) : []),
37
+ ]
38
+
14
39
  /**
15
40
  * Represents an API Gateway event for a request.
16
41
  * @type {APIGatewayEvent}
@@ -20,6 +45,10 @@ export default class Request<InputType, PathParamsType, QueryParamsType> {
20
45
  * The context object for the current instance.
21
46
  */
22
47
  private context: Context
48
+ /**
49
+ * Cached result of whether this request is a health/monitoring endpoint.
50
+ */
51
+ private readonly _isHealthCheckPath: boolean
23
52
 
24
53
  /**
25
54
  * Constructs a new instance of the class.
@@ -31,8 +60,142 @@ export default class Request<InputType, PathParamsType, QueryParamsType> {
31
60
  constructor(requestEvent: APIGatewayEvent, context: Context, logger: Logger) {
32
61
  this.requestEvent = requestEvent
33
62
  this.context = context
34
- logger.info('Request info:', requestEvent)
35
- logger.debug('Request context:', context)
63
+
64
+ // Skip verbose logging for health check and monitoring endpoints
65
+ const path = requestEvent.path || ''
66
+ this._isHealthCheckPath = this.isHealthOrMonitoringPath(path)
67
+ if (!this._isHealthCheckPath) {
68
+ logger.info('Request:', this.getRelevantLogData(requestEvent, context))
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Builds a concise, sanitized request summary for logging.
74
+ * Avoid logging the full event object, raw body, or sensitive header values.
75
+ */
76
+ private getRelevantLogData(requestEvent: APIGatewayEvent, context: Context): Record<string, any> {
77
+ if (this.isQueueEvent(requestEvent)) {
78
+ return this.getQueueEventLogData(requestEvent, context)
79
+ }
80
+
81
+ const headers = requestEvent.headers || {}
82
+ const queryParams = requestEvent.queryStringParameters || {}
83
+ const pathParams = requestEvent.pathParameters || {}
84
+ const body = this.getBody()
85
+
86
+ return {
87
+ method: requestEvent.httpMethod,
88
+ path: requestEvent.path,
89
+ stage: requestEvent.requestContext?.stage,
90
+ requestId: context.awsRequestId || requestEvent.requestContext?.requestId,
91
+ sourceIp: requestEvent.requestContext?.identity?.sourceIp || headers['x-forwarded-for'],
92
+ userAgent: headers['User-Agent'] || headers['user-agent'],
93
+ contentType: headers['Content-Type'] || headers['content-type'],
94
+ contentLength: headers['Content-Length'] || headers['content-length'],
95
+ ...(Object.keys(pathParams).length > 0 ? { pathParams } : {}),
96
+ ...(Object.keys(queryParams).length > 0 ? { queryParams } : {}),
97
+ ...this.getBodyLogData(body),
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Detects queue-style event payloads such as SQS events.
103
+ */
104
+ private isQueueEvent(requestEvent: any): requestEvent is {
105
+ Records: Array<Record<string, any>>
106
+ } {
107
+ return Array.isArray(requestEvent?.Records)
108
+ }
109
+
110
+ /**
111
+ * Builds a concise log payload for SQS-style record batches.
112
+ */
113
+ private getQueueEventLogData(
114
+ requestEvent: { Records: Array<Record<string, any>> },
115
+ context: Context
116
+ ): Record<string, any> {
117
+ const records = requestEvent.Records || []
118
+ const eventSources = this.getUniqueRecordValues(records, 'eventSource')
119
+ const queueArns = this.getUniqueRecordValues(records, 'eventSourceARN')
120
+ const regions = this.getUniqueRecordValues(records, 'awsRegion')
121
+ const messageIds = records.map(record => record.messageId).filter(Boolean)
122
+ const messageGroupIds = records.map(record => record.attributes?.MessageGroupId).filter(Boolean)
123
+ const bodySummary = records.map(record =>
124
+ this.getBodySummary(this.parseRecordBody(record.body))
125
+ )
126
+
127
+ return {
128
+ eventType: eventSources.length === 1 ? eventSources[0] : eventSources,
129
+ recordCount: records.length,
130
+ requestId: context.awsRequestId,
131
+ ...(queueArns.length > 0
132
+ ? { queueArn: queueArns.length === 1 ? queueArns[0] : queueArns }
133
+ : {}),
134
+ ...(regions.length > 0 ? { region: regions.length === 1 ? regions[0] : regions } : {}),
135
+ ...(messageIds.length > 0 ? { messageIds } : {}),
136
+ ...(messageGroupIds.length > 0 ? { messageGroupIds: [...new Set(messageGroupIds)] } : {}),
137
+ body: records.length === 1 ? bodySummary[0] : bodySummary,
138
+ }
139
+ }
140
+
141
+ private getUniqueRecordValues(records: Array<Record<string, any>>, key: string): string[] {
142
+ return [...new Set(records.map(record => record[key]).filter(Boolean))]
143
+ }
144
+
145
+ /**
146
+ * Parses record bodies when they are JSON strings; otherwise returns the raw value.
147
+ */
148
+ private parseRecordBody(body: unknown): unknown {
149
+ if (typeof body !== 'string') return body
150
+
151
+ try {
152
+ return JSON.parse(body)
153
+ } catch {
154
+ return body
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Logs body shape instead of full request payload to keep logs useful and safe.
160
+ */
161
+ private getBodyLogData(body: unknown): Record<string, any> {
162
+ if (body == null) return {}
163
+
164
+ return { body: this.getBodySummary(body) }
165
+ }
166
+
167
+ private getBodySummary(body: unknown): Record<string, any> {
168
+ if (body == null) return { type: 'null' }
169
+
170
+ if (Array.isArray(body)) {
171
+ return { type: 'array', size: body.length }
172
+ }
173
+
174
+ if (Buffer.isBuffer(body)) {
175
+ return { type: 'buffer', size: body.length }
176
+ }
177
+
178
+ if (typeof body === 'object') {
179
+ return { type: 'object', keys: Object.keys(body as Record<string, unknown>) }
180
+ }
181
+
182
+ if (typeof body === 'string') {
183
+ return { type: 'string', size: body.length }
184
+ }
185
+
186
+ return { type: typeof body }
187
+ }
188
+
189
+ /**
190
+ * Checks if the path is a health check or monitoring endpoint that should not be logged verbosely.
191
+ * @param {string} path - The request path
192
+ * @returns {boolean} - True if it's a health/monitoring endpoint
193
+ */
194
+ private isHealthOrMonitoringPath(path: string): boolean {
195
+ const lowerPath = path.toLowerCase()
196
+ return Request.IGNORED_PATHS.some(
197
+ ignored => lowerPath === ignored || lowerPath.startsWith(ignored + '/')
198
+ )
36
199
  }
37
200
 
38
201
  /**
@@ -122,6 +285,17 @@ export default class Request<InputType, PathParamsType, QueryParamsType> {
122
285
  return this.requestEvent.path
123
286
  }
124
287
 
288
+ public isHealthCheckPath(): boolean {
289
+ return this._isHealthCheckPath
290
+ }
291
+
292
+ public static isHealthCheckPath(path: string): boolean {
293
+ const lowerPath = (path || '').toLowerCase()
294
+ return Request.IGNORED_PATHS.some(
295
+ ignored => lowerPath === ignored || lowerPath.startsWith(ignored + '/')
296
+ )
297
+ }
298
+
125
299
  /**
126
300
  * Retrieves the HTTP method of the current request.
127
301
  * @returns {string} The HTTP method of the request.
@@ -140,7 +140,11 @@ export default class Transaction<
140
140
  this.syncReturn = !!config?.syncReturn
141
141
  this.retrowErrors = !!config?.throwOnErrors /* retrow internal errors */
142
142
  // components
143
- this.logger = new Logger(config?.logger, transactionId)
143
+ const isHealthCheck = Request.isHealthCheckPath((<APIGatewayEvent>event).path || '')
144
+ this.logger = new Logger(
145
+ { ...config?.logger, silent: isHealthCheck || !!config?.logger?.silent },
146
+ transactionId
147
+ )
144
148
  this.request = new Request<InputType, PathParamsType, QueryParamsType>(
145
149
  this.event,
146
150
  this.context,
@@ -51,6 +51,8 @@ const blacklist = ['password', 'token', 'accounts', 'authorization', 'key'].map(
51
51
  export type LoggerConfig = {
52
52
  sensitiveFilteringKeywords?: boolean | Array<string>
53
53
  logLevel?: LOG_LEVELS | string
54
+ /** When true, all log output is suppressed (e.g. health check routes). */
55
+ silent?: boolean
54
56
  }
55
57
 
56
58
  type SupressableItem = {
@@ -113,8 +115,10 @@ export default class Logger {
113
115
  //
114
116
  this.setupBindings()
115
117
  //
116
- this.log('Using logger with level: ' + this._LOG_LEVEL.toString())
117
- this.debug('logger config: ', this.config)
118
+ if (!this.config.silent) {
119
+ this.log('Using logger with level: ' + this._LOG_LEVEL.toString())
120
+ this.debug('logger config: ', this.config)
121
+ }
118
122
  }
119
123
 
120
124
  /**
@@ -245,6 +249,7 @@ export default class Logger {
245
249
  * @returns None
246
250
  */
247
251
  private processLog(level: LOG_LEVELS, args: any): void {
252
+ if (this.config?.silent) return
248
253
  if (level < this._LOG_LEVEL) return
249
254
  //get args
250
255
  const msg: string[] = []
@@ -195,6 +195,12 @@ export type RouterConfig = TransactionConfig & {
195
195
  * @type {string | undefined}
196
196
  */
197
197
  healthCheckRoute?: string
198
+ /**
199
+ * Paths that should not be logged (e.g., health checks, metrics).
200
+ * These requests will still be processed but won't create verbose logs.
201
+ * @type {string[] | undefined}
202
+ */
203
+ noLogPaths?: string[]
198
204
  /**
199
205
  * Global rate limiting configuration for all routes.
200
206
  * Individual routes can override this with their own rateLimit config.
@@ -40,7 +40,14 @@ export default class ContainerServer extends Server {
40
40
  * @returns {Promise<void>} - A promise that resolves when the proxy is loaded.
41
41
  */
42
42
  public async start() {
43
- await this.proxy.load()
43
+ try {
44
+ console.log('[ContainerServer] Starting server...')
45
+ await this.proxy.load()
46
+ console.log('[ContainerServer] Server started successfully')
47
+ } catch (error) {
48
+ console.error('[ContainerServer] Failed to start server:', error)
49
+ throw error
50
+ }
44
51
  }
45
52
 
46
53
  /**
@@ -49,7 +56,15 @@ export default class ContainerServer extends Server {
49
56
  * @returns {Promise<void>} - A promise that resolves once the proxy is unloaded.
50
57
  */
51
58
  public async stop(err?: any) {
52
- await this.proxy.unload(err)
59
+ try {
60
+ console.log('[ContainerServer] Stopping server...')
61
+ await this.proxy.unload(err)
62
+ console.log('[ContainerServer] Server stopped successfully')
63
+ } catch (stopError) {
64
+ console.error('[ContainerServer] Error during server shutdown:', stopError)
65
+ // Force exit if graceful shutdown fails
66
+ process.exit(1)
67
+ }
53
68
  }
54
69
 
55
70
  /**
@@ -58,8 +73,42 @@ export default class ContainerServer extends Server {
58
73
  * @returns None
59
74
  */
60
75
  private listenProcessEvents() {
61
- // start process listeners
62
- process.on('unhandledRejection', this.stop.bind(this)) // listen to exceptions
63
- process.on('SIGINT', this.stop.bind(this)) // listen on SIGINT signal and gracefully stop the server
76
+ // Handle unhandled promise rejections
77
+ process.on('unhandledRejection', (reason, promise) => {
78
+ console.error('[ContainerServer] CRITICAL: Unhandled Promise Rejection')
79
+ console.error('[ContainerServer] Reason:', reason)
80
+ if (reason instanceof Error) {
81
+ console.error('[ContainerServer] Stack:', reason.stack)
82
+ }
83
+ console.error('[ContainerServer] Promise:', promise)
84
+ console.error('[ContainerServer] Shutting down server due to unhandled rejection...')
85
+ this.stop(reason)
86
+ })
87
+
88
+ // Handle uncaught exceptions
89
+ process.on('uncaughtException', error => {
90
+ console.error('[ContainerServer] CRITICAL: Uncaught Exception')
91
+ console.error('[ContainerServer] Error:', error)
92
+ console.error('[ContainerServer] Stack:', error.stack)
93
+ console.error('[ContainerServer] Shutting down server due to uncaught exception...')
94
+ this.stop(error)
95
+ })
96
+
97
+ // Handle SIGINT (Ctrl+C)
98
+ process.on('SIGINT', () => {
99
+ console.log('[ContainerServer] SIGINT received, shutting down gracefully...')
100
+ this.stop()
101
+ })
102
+
103
+ // Handle SIGTERM (Docker/ECS stop)
104
+ process.on('SIGTERM', () => {
105
+ console.log('[ContainerServer] SIGTERM received, shutting down gracefully...')
106
+ this.stop()
107
+ })
108
+
109
+ // Log when process is about to exit
110
+ process.on('exit', code => {
111
+ console.log(`[ContainerServer] 🚪 Process exiting with code: ${code}`)
112
+ })
64
113
  }
65
114
  }
@@ -1,10 +1,14 @@
1
1
  import { Request, Response } from 'express'
2
2
 
3
3
  import GenericHandlerEvent, { GenericHandlerEventResponse } from './GenericHandlerEvent.js'
4
+ import WapiRequest from '../../../API/Request.js'
4
5
  import Globals from '../../../Globals.js'
6
+ import Logger from '../../../Logger/Logger.js'
5
7
  import Utils from '../../../Util/Utils.js'
6
8
  import Server from '../Server.js'
7
9
 
10
+ const logger = new Logger({ logLevel: 'INFO' }, 'proxy-container')
11
+
8
12
  /**
9
13
  * Creates an async function that handles serverless events and sends a response.
10
14
  * @param {Server['handleServerlessEvent']} serverlessHandler - The serverless handler function.
@@ -27,15 +31,17 @@ export default (serverlessHandler: Server['handleServerlessEvent']) => {
27
31
  // Respond
28
32
  processServerlessResponse(invokationResp, res)
29
33
  } catch (e) {
30
- console.error('[Proxy] - Exception during execution!', e)
31
- console.error(e.stack)
34
+ logger.error('[Proxy] - Exception during execution!', e)
35
+ logger.error(e.stack)
32
36
  res.status(Globals.Resp_STATUSCODE_EXCEPTION).json({
33
37
  ...e,
34
38
  err: Globals.Resp_MSG_EXCEPTION,
35
39
  errCode: Globals.Resp_CODE_EXCEPTION,
36
40
  })
37
41
  }
38
- console.info(`[Proxy] - Request took ${Date.now() - startTime}ms`)
42
+ if (!WapiRequest.isHealthCheckPath(request.path)) {
43
+ logger.info(`[Proxy] - Request took ${Date.now() - startTime}ms`)
44
+ }
39
45
  }
40
46
  }
41
47
 
@@ -0,0 +1,13 @@
1
+ import { mockClient } from 'aws-sdk-client-mock'
2
+
3
+ export function mockAwsClient<TClient>(client: TClient) {
4
+ return mockClient(client as any) as any
5
+ }
6
+
7
+ export function mockAwsCommand<TCommand>(command: TCommand) {
8
+ return command as any
9
+ }
10
+
11
+ export function mockAwsResult<TResult>(result: TResult) {
12
+ return result as any
13
+ }
@@ -1,11 +1,11 @@
1
1
  import { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm'
2
- import { mockClient } from 'aws-sdk-client-mock'
3
2
  import { expect as c_expect } from 'chai'
4
3
 
5
4
  import Configuration from '../../src/Config/Configuration.js'
5
+ import { mockAwsClient, mockAwsCommand, mockAwsResult } from '../AwsSdkTest.utils.js'
6
6
  import { SampleConfig } from '../Test.utils.js'
7
7
 
8
- const SSMMock = mockClient(SSMClient)
8
+ const SSMMock = mockAwsClient(SSMClient)
9
9
  const config = new Configuration<typeof SampleConfig>(SampleConfig, 'my-prefix')
10
10
  describe(`Optional remote environment`, () => {
11
11
  // reset mock
@@ -27,35 +27,39 @@ describe(`Optional remote environment`, () => {
27
27
  })
28
28
 
29
29
  test('Optional value with null value', async () => {
30
- SSMMock.on(GetParameterCommand).resolves({
31
- Parameter: {
32
- Value: undefined,
33
- },
34
- })
30
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).resolves(
31
+ mockAwsResult({
32
+ Parameter: {
33
+ Value: undefined,
34
+ },
35
+ })
36
+ )
35
37
  const token = await config.asyncGet('TOKEN_SECRET_FALSY')
36
38
  c_expect(token).is.undefined
37
39
  })
38
40
 
39
41
  test('Optional value with empty response', async () => {
40
- SSMMock.on(GetParameterCommand).resolves({})
42
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).resolves(mockAwsResult({}))
41
43
  const token = await config.asyncGet('TOKEN_SECRET_FALSY')
42
44
  c_expect(token).is.undefined
43
45
  })
44
46
 
45
47
  test('Optional value with valid response', async () => {
46
48
  const value = 'abc'
47
- SSMMock.on(GetParameterCommand).resolves({
48
- Parameter: {
49
- Value: value,
50
- },
51
- })
49
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).resolves(
50
+ mockAwsResult({
51
+ Parameter: {
52
+ Value: value,
53
+ },
54
+ })
55
+ )
52
56
  const token = await config.asyncGet('TOKEN_SECRET_FALSY')
53
57
  c_expect(token).is.not.undefined
54
58
  c_expect(token).to.be.equals(value)
55
59
  })
56
60
 
57
61
  test('Optional value rejection', async () => {
58
- SSMMock.on(GetParameterCommand).rejects(new Error('failed!'))
62
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).rejects(new Error('failed!'))
59
63
  const token = await config.asyncGet('TOKEN_SECRET_FALSY')
60
64
  c_expect(token).is.undefined
61
65
  })
@@ -81,11 +85,13 @@ describe(`Required remote environment`, () => {
81
85
  })
82
86
 
83
87
  test('Required value with null value', async () => {
84
- SSMMock.on(GetParameterCommand).resolves({
85
- Parameter: {
86
- Value: undefined,
87
- },
88
- })
88
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).resolves(
89
+ mockAwsResult({
90
+ Parameter: {
91
+ Value: undefined,
92
+ },
93
+ })
94
+ )
89
95
  let err = null
90
96
  try {
91
97
  await config.asyncGet('TOKEN_SECRET')
@@ -97,7 +103,7 @@ describe(`Required remote environment`, () => {
97
103
  })
98
104
 
99
105
  test('Required value with empty response', async () => {
100
- SSMMock.on(GetParameterCommand).resolves({})
106
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).resolves(mockAwsResult({}))
101
107
  let err = null
102
108
  try {
103
109
  await config.asyncGet('TOKEN_SECRET')
@@ -110,18 +116,20 @@ describe(`Required remote environment`, () => {
110
116
 
111
117
  test('Required value with valid response', async () => {
112
118
  const value = 'abc'
113
- SSMMock.on(GetParameterCommand).resolves({
114
- Parameter: {
115
- Value: value,
116
- },
117
- })
119
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).resolves(
120
+ mockAwsResult({
121
+ Parameter: {
122
+ Value: value,
123
+ },
124
+ })
125
+ )
118
126
  const token = await config.asyncGet('TOKEN_SECRET')
119
127
  c_expect(token).is.not.undefined
120
128
  c_expect(token).to.be.equals(value)
121
129
  })
122
130
 
123
131
  test('Required value rejection', async () => {
124
- SSMMock.on(GetParameterCommand).rejects(new Error('failed!'))
132
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).rejects(new Error('failed!'))
125
133
  let err = null
126
134
  try {
127
135
  await config.asyncGet('TOKEN_SECRET')
@@ -185,17 +193,19 @@ describe(`Caching test`, () => {
185
193
  const value = '123'
186
194
  const key = 'TOKEN_SECRET'
187
195
  // initial fetch
188
- SSMMock.on(GetParameterCommand).resolves({
189
- Parameter: {
190
- Value: value,
191
- },
192
- })
196
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).resolves(
197
+ mockAwsResult({
198
+ Parameter: {
199
+ Value: value,
200
+ },
201
+ })
202
+ )
193
203
  const v = await config.asyncGet(key)
194
204
  c_expect(v).is.not.null
195
205
  c_expect(v).to.be.equals(value)
196
206
  // subsequent fetch
197
207
  SSMMock.reset()
198
- SSMMock.on(GetParameterCommand).rejects(new Error('failed!'))
208
+ SSMMock.on(mockAwsCommand(GetParameterCommand)).rejects(new Error('failed!'))
199
209
  const v2 = await config.asyncGet(key)
200
210
  c_expect(v2).is.not.null
201
211
  c_expect(v2).to.be.equals(value)