@spytecgps/lambda-utils 2.3.28 → 3.0.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.
@@ -7,6 +7,7 @@ export type EnvConfigsParam<T> = {
7
7
  [k: string]: DeepPartial<T>;
8
8
  };
9
9
  export declare const getEnvKey: () => Env;
10
+ export declare const isOffline: () => boolean;
10
11
  export declare const isLocal: () => boolean;
11
12
  export declare const isTest: () => boolean;
12
13
  export declare const isDev: () => boolean;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- export * from './wrappers';
1
+ export * from './config';
2
2
  export * from './errors';
3
- export * from './validation';
4
- export * from './types';
5
3
  export * from './middleware';
4
+ export * from './types';
6
5
  export * from './utils';
7
- export * from './config';
6
+ export * from './validation';
package/dist/index.js CHANGED
@@ -1,18 +1,17 @@
1
1
  'use strict';
2
2
 
3
- var sdkLogger = require('@spytecgps/sdk-logger');
4
- var dayjs = require('dayjs');
5
- var timezone = require('dayjs/plugin/timezone');
6
- var utc = require('dayjs/plugin/utc');
7
- var Joi = require('joi');
8
- var qs = require('qs');
9
- var rawMiddy = require('@middy/core');
3
+ var merge = require('deepmerge');
4
+ var core = require('@middy/core');
10
5
  var httpErrorHandler = require('@middy/http-error-handler');
11
6
  var httpResponseSerializer = require('@middy/http-response-serializer');
12
7
  var sqsJsonBodyParser = require('@middy/sqs-json-body-parser');
13
- var merge = require('deepmerge');
14
8
  var inputOutputLogger = require('@middy/input-output-logger');
15
9
  var clientLambda = require('@aws-sdk/client-lambda');
10
+ var dayjs = require('dayjs');
11
+ var timezone = require('dayjs/plugin/timezone');
12
+ var utc = require('dayjs/plugin/utc');
13
+ var Joi = require('joi');
14
+ var qs = require('qs');
16
15
 
17
16
  function _interopNamespaceDefault(e) {
18
17
  var n = Object.create(null);
@@ -34,180 +33,24 @@ function _interopNamespaceDefault(e) {
34
33
  var Joi__namespace = /*#__PURE__*/_interopNamespaceDefault(Joi);
35
34
  var qs__namespace = /*#__PURE__*/_interopNamespaceDefault(qs);
36
35
 
37
- dayjs.extend(utc);
38
- dayjs.extend(timezone);
39
- const json = Joi__namespace.extend((joi) => {
40
- return {
41
- type: 'object',
42
- base: joi.object(),
43
- messages: {
44
- 'json.valid': 'must be valid JSON',
45
- },
46
- coerce(value) {
47
- try {
48
- return { value: JSON.parse(value) };
49
- }
50
- catch (err) {
51
- return null;
52
- }
53
- },
54
- validate(value, helpers) {
55
- if (!value) {
56
- return { value, errors: helpers.error('json.valid') };
57
- }
58
- return { value };
59
- },
60
- };
61
- });
62
- const urlEncoded = Joi__namespace.extend((joi) => {
63
- return {
64
- type: 'object',
65
- base: joi.object(),
66
- coerce(value) {
67
- return { value: qs__namespace.parse(value) };
68
- },
69
- };
70
- });
71
- const imeiSchema = Joi__namespace.string()
72
- .regex(/^\d{15,16}$/)
73
- .message('Invalid IMEI');
74
- const iccidSchema = Joi__namespace.string()
75
- .regex(/^[0-9A-Za-z]{18,22}$/)
76
- .message('Invalid ICCID');
77
- const SpytecJoi = Joi__namespace.extend((joi) => ({
78
- type: 'imei',
79
- messages: 'Invalid IMEI',
80
- base: joi.string().regex(/^\d{15,16}$/),
81
- }), (joi) => ({
82
- type: 'iccid',
83
- messages: 'Invalid ICCID',
84
- base: joi.string().regex(/^[0-9A-Za-z]{18,22}$/),
85
- }), (joi) => ({
86
- type: 'urlEncodedObject',
87
- base: joi.object(),
88
- coerce(value) {
89
- return { value: qs__namespace.parse(value) };
90
- },
91
- }), (joi) => ({
92
- type: 'jsonObject',
93
- base: joi.object(),
94
- coerce(value) {
95
- try {
96
- return { value: JSON.parse(value) };
97
- }
98
- catch (err) {
99
- return null;
100
- }
101
- },
102
- validate(value, helpers) {
103
- if (!value) {
104
- return { value, errors: helpers.error('json.valid') };
105
- }
106
- return { value };
107
- },
108
- }), (joi) => ({
109
- type: 'delimitedArray',
110
- base: joi.array().default([]),
111
- coerce: (value) => ({
112
- value: value.split ? value.split(',') : value,
113
- }),
114
- }), (joi) => ({
115
- type: 'queryStringParameters',
116
- messages: 'Missing query parameters',
117
- base: joi.object().required(),
118
- }), (joi) => ({
119
- type: 'date',
120
- base: joi.date(),
121
- prepare(value, helpers) {
122
- try {
123
- const dayjsDate = dayjs.tz(value, 'UTC');
124
- if (dayjsDate.isValid()) {
125
- return { value: dayjsDate.toDate() };
126
- }
127
- }
128
- catch (error) {
129
- return helpers.error('any.invalid');
130
- }
131
- },
132
- }), (joi) => ({
133
- type: 'jsonArray',
134
- base: joi.array(),
135
- coerce(value) {
136
- try {
137
- return { value: JSON.parse(value) };
138
- }
139
- catch (err) {
140
- return { value: null };
141
- }
142
- },
143
- validate(value, helpers) {
144
- if (!Array.isArray(value)) {
145
- return { value, errors: helpers.error('jsonArray.schema') };
146
- }
147
- return { value };
148
- },
149
- }), (joi) => ({
150
- type: 'base64ThenUriEncodedObject',
151
- base: joi.object(),
152
- coerce(value) {
153
- try {
154
- const decodedValue = decodeURIComponent(Buffer.from(value, 'base64').toString());
155
- return { value: JSON.parse(decodedValue) };
156
- }
157
- catch (err) {
158
- return null;
159
- }
160
- },
161
- validate(value, helpers) {
162
- if (!value) {
163
- return { value, errors: helpers.error('json.valid') };
164
- }
165
- return { value };
166
- },
167
- }));
168
-
169
- const getAuthorizerValidator = (params = {}) => {
170
- return Joi__namespace.object({
171
- clientId: Joi__namespace.number().greater(0).required(),
172
- userId: Joi__namespace.string().guid( /*{ version: 'uuidv4' }*/).required(),
173
- resources: json.object({}),
174
- scope: Joi__namespace.string().optional(),
175
- // .error(() => new UnauthorizedError(`missing scope ${scope}`))
176
- type: Joi__namespace.string().optional(),
177
- // .error(() => new UnauthorizedError(`missing user type ${type}`))
178
- enterprise: Joi__namespace.boolean().default(false),
179
- maintenanceModule: Joi__namespace.boolean().default(false),
180
- billingMethod: Joi__namespace.string().optional(),
181
- customerSegment: Joi__namespace.string().optional(),
182
- securityGroupTagId: Joi__namespace.number().optional().allow(null),
183
- securityRole: Joi__namespace.string().optional().allow(null),
184
- ...params,
185
- });
186
- };
187
- const getAuthorizerValidatorV4 = (params = {}) => {
188
- return Joi__namespace.object({
189
- clientId: Joi__namespace.number().greater(0).required(),
190
- userId: Joi__namespace.string().guid( /*{ version: 'uuidv4' }*/).required(),
191
- scope: Joi__namespace.string().optional(),
192
- type: Joi__namespace.string().optional(),
193
- ...params,
194
- });
195
- };
196
- /**
197
- * @deprecated
198
- */
199
- const requestContextValidator = Joi__namespace.object({
200
- authorizer: getAuthorizerValidator(),
201
- });
202
- const getRequestContextValidator = (params = {}) => {
203
- return Joi__namespace.object({
204
- authorizer: getAuthorizerValidator(params),
205
- });
36
+ const getEnvKey = () => {
37
+ if (process.env.NODE_ENV === 'test') {
38
+ return 'test';
39
+ }
40
+ if (isOffline() && process.env.STAGE === 'dev') {
41
+ return 'local';
42
+ }
43
+ return (process.env.STAGE ?? process.env.NODE_ENV ?? 'dev');
206
44
  };
207
- const getRequestContextValidatorV4 = (params = {}) => {
208
- return Joi__namespace.object({
209
- authorizer: getAuthorizerValidatorV4(params),
210
- });
45
+ const isOffline = () => !!process.env.IS_OFFLINE;
46
+ const isLocal = () => getEnvKey() === 'local';
47
+ const isTest = () => getEnvKey() === 'test';
48
+ const isDev = () => getEnvKey() === 'dev';
49
+ const isProduction = () => getEnvKey() === 'prod';
50
+ const setupEnvConfig = (envConfigs) => {
51
+ const baseConfig = envConfigs['base'];
52
+ const envConfig = envConfigs[getEnvKey()] ?? {};
53
+ return merge(baseConfig, envConfig);
211
54
  };
212
55
 
213
56
  class HttpError extends Error {
@@ -237,193 +80,22 @@ class ForbiddenError extends HttpError {
237
80
  }
238
81
 
239
82
  class NotFoundError extends HttpError {
240
- code = 404;
241
- statusCode = 404;
242
- name = 'NotFoundError';
243
- }
244
-
245
- class UnauthorizedError extends HttpError {
246
- code = 401;
247
- statusCode = 401;
248
- name = 'UnauthorizedError';
249
- }
250
-
251
- const validateEvent = (event, schema, validateOptions) => {
252
- if (!schema) {
253
- sdkLogger.logger.warn(`skipping validation`);
254
- return event;
255
- }
256
- const { error, value } = schema.validate(event, {
257
- allowUnknown: validateOptions?.allowUnknown || true,
258
- errors: {
259
- label: 'key',
260
- wrap: {
261
- label: false,
262
- },
263
- },
264
- });
265
- if (error) {
266
- sdkLogger.logger.error({ error }, 'Validation error');
267
- throw error.isJoi ? new BadRequestError(error.message) : error;
268
- }
269
- return value;
270
- };
271
-
272
- const defaultApiSchema = SpytecJoi.object({
273
- requestContext: getRequestContextValidator(),
274
- });
275
-
276
- const baseHeaders = {
277
- 'Content-Type': 'application/json',
278
- 'Access-Control-Allow-Origin': '*',
279
- 'Access-Control-Allow-Credentials': true,
280
- };
281
- const buildResponseBody = (statusCode, message, data) => {
282
- return {
283
- success: statusCode < 400,
284
- message,
285
- result: typeof data !== 'undefined' ? data : undefined,
286
- };
287
- };
288
- const buildProxyResult = ({ statusCode = 200, message = 'ok', data, headers = {}, multiValueHeaders = {}, rawResult = false, stringifyBody = true, }) => {
289
- const resp = rawResult ? data : buildResponseBody(statusCode, message, data);
290
- const body = stringifyBody ? resp && JSON.stringify(resp) : data;
291
- return {
292
- headers: { ...baseHeaders, ...headers },
293
- multiValueHeaders,
294
- statusCode,
295
- body,
296
- };
297
- };
298
-
299
- const logEvent$2 = (e) => ({
300
- resource: e.resource,
301
- httpMethod: e.httpMethod,
302
- queryStringParameters: e.queryStringParameters,
303
- pathParameters: e.pathParameters,
304
- body: e.body,
305
- });
306
- const apiGatewayEventWrapper = async ({ event, context, schema, handler, }) => {
307
- if (event && context) {
308
- sdkLogger.withRequest(event, context);
309
- }
310
- try {
311
- const validatedEvent = validateEvent(event, schema);
312
- const result = await handler(validatedEvent);
313
- return buildProxyResult(result);
314
- }
315
- catch (err) {
316
- sdkLogger.logger.error({ err, event: logEvent$2(event) }, `apiGatewayWrapper - caught error`);
317
- return buildProxyResult({
318
- statusCode: err.code || 500,
319
- message: err.message || 'Error',
320
- });
321
- }
322
- };
323
-
324
- const logEvent$1 = (e) => (e.Records || []).map((record) => ({
325
- messageId: record.messageId,
326
- body: record.body,
327
- messageAttributes: record.messageAttributes,
328
- }));
329
- async function processEvent$1(validatedEvent, handler, singleHandler, mode) {
330
- if (!(handler || singleHandler)) {
331
- throw new Error(`handler or singleHandler not defined`);
332
- }
333
- if (handler) {
334
- await handler(validatedEvent);
335
- }
336
- else if (singleHandler) {
337
- const records = validatedEvent.Records;
338
- if (mode === 'serial') {
339
- for (const record of records) {
340
- await singleHandler(record);
341
- }
342
- }
343
- else if (mode === 'parallel') {
344
- await Promise.all(records.map((record) => singleHandler(record)));
345
- }
346
- }
347
- }
348
- const sqsEventWrapper$1 = async ({ event, context, schema, handler, singleHandler, mode = 'serial', }) => {
349
- if (event && context) {
350
- sdkLogger.withRequest(event, context);
351
- }
352
- try {
353
- const validatedEvent = validateEvent(event, schema);
354
- await processEvent$1(validatedEvent, handler, singleHandler, mode);
355
- }
356
- catch (err) {
357
- sdkLogger.logger.error({ err, event: logEvent$1(event) }, `sqsEventWrapper - caught error`);
358
- throw err;
359
- }
360
- };
361
-
362
- const logEvent = (e) => (e.Records || []).map((record) => ({
363
- messageId: record.messageId,
364
- body: record.body,
365
- messageAttributes: record.messageAttributes,
366
- }));
367
- const processEvent = async (validatedEvent, handler, singleHandler, mode) => {
368
- if (!(handler || singleHandler)) {
369
- throw new Error(`handler or singleHandler not defined`);
370
- }
371
- if (handler) {
372
- return await handler(validatedEvent);
373
- }
374
- else if (singleHandler) {
375
- const records = validatedEvent.Records;
376
- if (mode === 'serial') {
377
- const result = [];
378
- for (const record of records) {
379
- const singleHandleResult = await singleHandler(record);
380
- result.push(singleHandleResult);
381
- }
382
- return result;
383
- }
384
- else if (mode === 'parallel') {
385
- return await Promise.all(records.map((record) => singleHandler(record)));
386
- }
387
- }
388
- };
389
- const sqsEventWrapper = async ({ event, context, schema, handler, singleHandler, mode = 'serial', }) => {
390
- if (event && context) {
391
- sdkLogger.withRequest(event, context);
392
- }
393
- try {
394
- const validatedEvent = validateEvent(event, schema);
395
- return await processEvent(validatedEvent, handler, singleHandler, mode);
396
- }
397
- catch (err) {
398
- sdkLogger.logger.error({ err, event: logEvent(event) }, `sqsEventWrapper - caught error`);
399
- throw err;
400
- }
401
- };
83
+ code = 404;
84
+ statusCode = 404;
85
+ name = 'NotFoundError';
86
+ }
402
87
 
403
- const getEnvKey = () => {
404
- if (process.env.NODE_ENV === 'test') {
405
- return 'test';
406
- }
407
- if (process.env.IS_OFFLINE && process.env.STAGE === 'dev') {
408
- return 'local';
409
- }
410
- return (process.env.STAGE ?? process.env.NODE_ENV ?? 'dev');
411
- };
412
- const isLocal = () => getEnvKey() === 'local';
413
- const isTest = () => getEnvKey() === 'test';
414
- const isDev = () => getEnvKey() === 'dev';
415
- const isProduction = () => getEnvKey() === 'prod';
416
- const setupEnvConfig = (envConfigs) => {
417
- const baseConfig = envConfigs['base'];
418
- const envConfig = envConfigs[getEnvKey()] ?? {};
419
- return merge(baseConfig, envConfig);
420
- };
88
+ class UnauthorizedError extends HttpError {
89
+ code = 401;
90
+ statusCode = 401;
91
+ name = 'UnauthorizedError';
92
+ }
421
93
 
422
94
  const AMAZON_TRACE_ID = '_X_AMZN_TRACE_ID';
423
95
  const CORRELATION_HEADER = 'x-correlation-';
424
96
  const CORRELATION_ID = `${CORRELATION_HEADER}id`;
425
97
  const CORRELATION_TRACE_ID = `${CORRELATION_HEADER}trace-id`;
426
- const contextualLogger = () => {
98
+ const contextualLogger = ({ logger }) => {
427
99
  const before = async ({ event, context }) => {
428
100
  const ctx = {
429
101
  awsRequestId: context?.awsRequestId,
@@ -451,15 +123,14 @@ const contextualLogger = () => {
451
123
  if (!ctx[CORRELATION_ID]) {
452
124
  ctx[CORRELATION_ID] = context.awsRequestId;
453
125
  }
454
- sdkLogger.logger.setHapnContext(ctx);
126
+ logger.setHapnContext(ctx);
455
127
  };
456
128
  return {
457
129
  before,
458
130
  };
459
131
  };
460
- const contextualLoggerMiddleware = contextualLogger();
461
132
 
462
- const ioLoggerMiddleware = inputOutputLogger({
133
+ const ioLogger = ({ logger }) => inputOutputLogger({
463
134
  omitPaths: [
464
135
  'event.multiValueHeaders',
465
136
  'event.multiValueQueryStringParameters',
@@ -487,8 +158,11 @@ const ioLoggerMiddleware = inputOutputLogger({
487
158
  'response.headers',
488
159
  ],
489
160
  logger: (req) => {
161
+ if (isLocal() || isTest()) {
162
+ return;
163
+ }
490
164
  const message = req?.event ? 'event' : 'response';
491
- sdkLogger.logger.info(req.event ?? req.response, message);
165
+ logger.info(req.event ?? req.response, message);
492
166
  },
493
167
  });
494
168
 
@@ -502,7 +176,7 @@ const normalizerMiddleware = () => {
502
176
  };
503
177
  };
504
178
 
505
- const offlineAuthMiddleware = ({ authFunctionName = 'spytec-web-api-auth-prod-AuthorizerFunction', enabled = !!process.env.IS_OFFLINE, } = {}) => {
179
+ const offlineAuth = ({ authFunctionName = 'spytec-web-api-auth-prod-AuthorizerFunction', enabled = isOffline(), logger, }) => {
506
180
  const lambdaClient = new clientLambda.LambdaClient({ region: process.env.AWS_REGION });
507
181
  return {
508
182
  before: async (request) => {
@@ -514,7 +188,7 @@ const offlineAuthMiddleware = ({ authFunctionName = 'spytec-web-api-auth-prod-Au
514
188
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
515
189
  // throw new Error('Authorization header is missing or invalid')
516
190
  // if we can't extract the token, this is is a public route, ignore it
517
- sdkLogger.logger.warn('Authorization header is missing or invalid, skipping fake offline authorization');
191
+ logger.warn('Authorization header is missing or invalid, skipping fake offline authorization');
518
192
  return;
519
193
  }
520
194
  const token = authHeader.slice(7); // Remove 'Bearer ' prefix
@@ -529,21 +203,44 @@ const offlineAuthMiddleware = ({ authFunctionName = 'spytec-web-api-auth-prod-Au
529
203
  });
530
204
  try {
531
205
  const response = await lambdaClient.send(command);
532
- const responsePayload = JSON.parse(Buffer.from(response.Payload).toString());
206
+ const responsePayload = JSON.parse(Buffer.from(response.Payload ?? '').toString());
533
207
  if (responsePayload.errorMessage) {
534
208
  throw new Error(responsePayload.errorMessage);
535
209
  }
536
210
  event.requestContext.authorizer = responsePayload.context;
537
211
  }
538
212
  catch (error) {
539
- sdkLogger.logger.error('Error invoking auth function:', error);
213
+ logger.error(error, 'Error invoking auth function');
540
214
  throw new Error('Authorization failed');
541
215
  }
542
216
  },
543
217
  };
544
218
  };
545
219
 
546
- const responseWrapperMiddleware = () => {
220
+ const baseHeaders = {
221
+ 'Content-Type': 'application/json',
222
+ 'Access-Control-Allow-Origin': '*',
223
+ 'Access-Control-Allow-Credentials': true,
224
+ };
225
+ const buildResponseBody = (statusCode, message, data) => {
226
+ return {
227
+ success: statusCode < 400,
228
+ message,
229
+ result: typeof data !== 'undefined' ? data : undefined,
230
+ };
231
+ };
232
+ const buildProxyResult = ({ statusCode = 200, message = 'ok', data, headers = {}, multiValueHeaders = {}, rawResult = false, stringifyBody = true, }) => {
233
+ const resp = rawResult ? data : buildResponseBody(statusCode, message, data);
234
+ const body = stringifyBody ? resp && JSON.stringify(resp) : data;
235
+ return {
236
+ headers: { ...baseHeaders, ...headers },
237
+ multiValueHeaders,
238
+ statusCode,
239
+ body,
240
+ };
241
+ };
242
+
243
+ const responseWrapper = ({ logger }) => {
547
244
  const responseWrapperMiddlewareAfter = (req) => {
548
245
  req.response = buildProxyResult(req.response);
549
246
  };
@@ -551,7 +248,7 @@ const responseWrapperMiddleware = () => {
551
248
  const statusCode = req.error?.code ?? 500;
552
249
  const errorMessage = req.error?.message || 'Error';
553
250
  const loggerMethod = statusCode >= 500 ? 'error' : 'info';
554
- sdkLogger.logger[loggerMethod](req.error, 'Request failed');
251
+ logger[loggerMethod](req.error, 'Request failed');
555
252
  req.response = buildProxyResult({
556
253
  statusCode,
557
254
  message: errorMessage,
@@ -563,7 +260,7 @@ const responseWrapperMiddleware = () => {
563
260
  };
564
261
  };
565
262
 
566
- const validatorMiddleware = ({ schema, allowUnknown = true }) => {
263
+ const validator = ({ schema, allowUnknown = true, logger }) => {
567
264
  const validatorMiddlewareBefore = (request) => {
568
265
  const { error, value } = schema.validate(request.event, {
569
266
  allowUnknown,
@@ -575,7 +272,7 @@ const validatorMiddleware = ({ schema, allowUnknown = true }) => {
575
272
  },
576
273
  });
577
274
  if (error) {
578
- sdkLogger.logger.error({ error }, 'Validation error');
275
+ logger.warn(error, 'Validation error');
579
276
  throw error.isJoi ? new BadRequestError(error.message) : error;
580
277
  }
581
278
  request.event = value;
@@ -601,12 +298,14 @@ const warmupMiddleware = (opt = {}) => {
601
298
  };
602
299
  };
603
300
 
604
- const baseMiddlewares = [
301
+ // Function that returns base middlewares with required logger
302
+ const getBaseMiddlewares = ({ logger }) => [
605
303
  warmupMiddleware(),
606
- contextualLoggerMiddleware,
607
- !(isLocal() || isTest()) ? ioLoggerMiddleware : undefined,
608
- ].filter(Boolean);
609
- const apiGatewayMiddlewares = [
304
+ contextualLogger({ logger }),
305
+ ioLogger({ logger }),
306
+ ];
307
+ // Function that returns API Gateway middlewares with required logger
308
+ const getApiGatewayMiddlewares = ({ logger }) => [
610
309
  httpResponseSerializer({
611
310
  serializers: [
612
311
  {
@@ -624,12 +323,10 @@ const apiGatewayMiddlewares = [
624
323
  ],
625
324
  default: 'application/json',
626
325
  }),
627
- responseWrapperMiddleware(),
628
- offlineAuthMiddleware(),
326
+ responseWrapper({ logger }),
327
+ offlineAuth({ logger }),
629
328
  normalizerMiddleware(),
630
329
  ];
631
- const middy = (handler) => rawMiddy(handler).use([...baseMiddlewares]);
632
- const apiGatewayMiddy = (handler) => rawMiddy(handler).use([...baseMiddlewares, ...apiGatewayMiddlewares]);
633
330
 
634
331
  class LambdaCache {
635
332
  collectionName;
@@ -708,10 +405,215 @@ const promiseWithTimeout = (promise, ms, timeoutError = new Error('Promise timed
708
405
  //Adding comment to test
709
406
  };
710
407
 
408
+ dayjs.extend(utc);
409
+ dayjs.extend(timezone);
410
+ const json = Joi__namespace.extend((joi) => {
411
+ return {
412
+ type: 'object',
413
+ base: joi.object(),
414
+ messages: {
415
+ 'json.valid': 'must be valid JSON',
416
+ },
417
+ coerce(value) {
418
+ try {
419
+ return { value: JSON.parse(value) };
420
+ }
421
+ catch (err) {
422
+ return null;
423
+ }
424
+ },
425
+ validate(value, helpers) {
426
+ if (!value) {
427
+ return { value, errors: helpers.error('json.valid') };
428
+ }
429
+ return { value };
430
+ },
431
+ };
432
+ });
433
+ const urlEncoded = Joi__namespace.extend((joi) => {
434
+ return {
435
+ type: 'object',
436
+ base: joi.object(),
437
+ coerce(value) {
438
+ return { value: qs__namespace.parse(value) };
439
+ },
440
+ };
441
+ });
442
+ const imeiSchema = Joi__namespace.string()
443
+ .regex(/^\d{15,16}$/)
444
+ .message('Invalid IMEI');
445
+ const iccidSchema = Joi__namespace.string()
446
+ .regex(/^[0-9A-Za-z]{18,22}$/)
447
+ .message('Invalid ICCID');
448
+ const SpytecJoi = Joi__namespace.extend((joi) => ({
449
+ type: 'imei',
450
+ messages: 'Invalid IMEI',
451
+ base: joi.string().regex(/^\d{15,16}$/),
452
+ }), (joi) => ({
453
+ type: 'iccid',
454
+ messages: 'Invalid ICCID',
455
+ base: joi.string().regex(/^[0-9A-Za-z]{18,22}$/),
456
+ }), (joi) => ({
457
+ type: 'urlEncodedObject',
458
+ base: joi.object(),
459
+ coerce(value) {
460
+ return { value: qs__namespace.parse(value) };
461
+ },
462
+ }), (joi) => ({
463
+ type: 'jsonObject',
464
+ base: joi.object(),
465
+ coerce(value) {
466
+ try {
467
+ return { value: JSON.parse(value) };
468
+ }
469
+ catch (err) {
470
+ return null;
471
+ }
472
+ },
473
+ validate(value, helpers) {
474
+ if (!value) {
475
+ return { value, errors: helpers.error('json.valid') };
476
+ }
477
+ return { value };
478
+ },
479
+ }), (joi) => ({
480
+ type: 'delimitedArray',
481
+ base: joi.array().default([]),
482
+ coerce: (value) => ({
483
+ value: value.split ? value.split(',') : value,
484
+ }),
485
+ }), (joi) => ({
486
+ type: 'queryStringParameters',
487
+ messages: 'Missing query parameters',
488
+ base: joi.object().required(),
489
+ }), (joi) => ({
490
+ type: 'date',
491
+ base: joi.date(),
492
+ prepare(value, helpers) {
493
+ try {
494
+ const dayjsDate = dayjs.tz(value, 'UTC');
495
+ if (dayjsDate.isValid()) {
496
+ return { value: dayjsDate.toDate() };
497
+ }
498
+ }
499
+ catch (error) {
500
+ return helpers.error('any.invalid');
501
+ }
502
+ },
503
+ }), (joi) => ({
504
+ type: 'jsonArray',
505
+ base: joi.array(),
506
+ coerce(value) {
507
+ try {
508
+ return { value: JSON.parse(value) };
509
+ }
510
+ catch (err) {
511
+ return { value: null };
512
+ }
513
+ },
514
+ validate(value, helpers) {
515
+ if (!Array.isArray(value)) {
516
+ return { value, errors: helpers.error('jsonArray.schema') };
517
+ }
518
+ return { value };
519
+ },
520
+ }), (joi) => ({
521
+ type: 'base64ThenUriEncodedObject',
522
+ base: joi.object(),
523
+ coerce(value) {
524
+ try {
525
+ const decodedValue = decodeURIComponent(Buffer.from(value, 'base64').toString());
526
+ return { value: JSON.parse(decodedValue) };
527
+ }
528
+ catch (err) {
529
+ return null;
530
+ }
531
+ },
532
+ validate(value, helpers) {
533
+ if (!value) {
534
+ return { value, errors: helpers.error('json.valid') };
535
+ }
536
+ return { value };
537
+ },
538
+ }));
539
+
540
+ const getAuthorizerValidator = (params = {}) => {
541
+ return Joi__namespace.object({
542
+ clientId: Joi__namespace.number().greater(0).required(),
543
+ userId: Joi__namespace.string().guid( /*{ version: 'uuidv4' }*/).required(),
544
+ resources: json.object({}),
545
+ scope: Joi__namespace.string().optional(),
546
+ // .error(() => new UnauthorizedError(`missing scope ${scope}`))
547
+ type: Joi__namespace.string().optional(),
548
+ // .error(() => new UnauthorizedError(`missing user type ${type}`))
549
+ enterprise: Joi__namespace.boolean().default(false),
550
+ maintenanceModule: Joi__namespace.boolean().default(false),
551
+ billingMethod: Joi__namespace.string().optional(),
552
+ customerSegment: Joi__namespace.string().optional(),
553
+ securityGroupTagId: Joi__namespace.number().optional().allow(null),
554
+ securityRole: Joi__namespace.string().optional().allow(null),
555
+ ...params,
556
+ });
557
+ };
558
+ const getAuthorizerValidatorV4 = (params = {}) => {
559
+ return Joi__namespace.object({
560
+ clientId: Joi__namespace.number().greater(0).required(),
561
+ userId: Joi__namespace.string().guid( /*{ version: 'uuidv4' }*/).required(),
562
+ scope: Joi__namespace.string().optional(),
563
+ type: Joi__namespace.string().optional(),
564
+ ...params,
565
+ });
566
+ };
567
+ /**
568
+ * @deprecated
569
+ */
570
+ const requestContextValidator = Joi__namespace.object({
571
+ authorizer: getAuthorizerValidator(),
572
+ });
573
+ const getRequestContextValidator = (params = {}) => {
574
+ return Joi__namespace.object({
575
+ authorizer: getAuthorizerValidator(params),
576
+ });
577
+ };
578
+ const getRequestContextValidatorV4 = (params = {}) => {
579
+ return Joi__namespace.object({
580
+ authorizer: getAuthorizerValidatorV4(params),
581
+ });
582
+ };
583
+
584
+ /**
585
+ * @deprecated
586
+ */
587
+ const validateEvent = (event, schema, validateOptions) => {
588
+ if (!schema) {
589
+ console.warn(`skipping validation`);
590
+ return event;
591
+ }
592
+ const { error, value } = schema.validate(event, {
593
+ allowUnknown: validateOptions?.allowUnknown || true,
594
+ errors: {
595
+ label: 'key',
596
+ wrap: {
597
+ label: false,
598
+ },
599
+ },
600
+ });
601
+ if (error) {
602
+ console.error({ error }, 'Validation error');
603
+ throw error.isJoi ? new BadRequestError(error.message) : error;
604
+ }
605
+ return value;
606
+ };
607
+
608
+ const defaultApiSchema = SpytecJoi.object({
609
+ requestContext: getRequestContextValidator(),
610
+ });
611
+
612
+ exports.merge = merge;
613
+ exports.middy = core;
711
614
  exports.httpErrorHandler = httpErrorHandler;
712
615
  exports.httpResponseSerializer = httpResponseSerializer;
713
616
  exports.sqsJsonBodyParser = sqsJsonBodyParser;
714
- exports.merge = merge;
715
617
  exports.BadRequestError = BadRequestError;
716
618
  exports.BaseError = BaseError;
717
619
  exports.ConflictError = ConflictError;
@@ -721,33 +623,31 @@ exports.LambdaCache = LambdaCache;
721
623
  exports.NotFoundError = NotFoundError;
722
624
  exports.SpytecJoi = SpytecJoi;
723
625
  exports.UnauthorizedError = UnauthorizedError;
724
- exports.apiGatewayEventWrapper = apiGatewayEventWrapper;
725
- exports.apiGatewayMiddlewares = apiGatewayMiddlewares;
726
- exports.apiGatewayMiddy = apiGatewayMiddy;
727
- exports.baseMiddlewares = baseMiddlewares;
728
- exports.buildProxyResult = buildProxyResult;
729
- exports.buildResponseBody = buildResponseBody;
626
+ exports.contextualLogger = contextualLogger;
730
627
  exports.defaultApiSchema = defaultApiSchema;
628
+ exports.getApiGatewayMiddlewares = getApiGatewayMiddlewares;
731
629
  exports.getAuthorizerValidator = getAuthorizerValidator;
732
630
  exports.getAuthorizerValidatorV4 = getAuthorizerValidatorV4;
631
+ exports.getBaseMiddlewares = getBaseMiddlewares;
733
632
  exports.getEnvKey = getEnvKey;
734
633
  exports.getRequestContextValidator = getRequestContextValidator;
735
634
  exports.getRequestContextValidatorV4 = getRequestContextValidatorV4;
736
635
  exports.iccidSchema = iccidSchema;
737
636
  exports.imeiSchema = imeiSchema;
637
+ exports.ioLogger = ioLogger;
738
638
  exports.isDev = isDev;
739
639
  exports.isLocal = isLocal;
640
+ exports.isOffline = isOffline;
740
641
  exports.isProduction = isProduction;
741
642
  exports.isTest = isTest;
742
643
  exports.json = json;
743
- exports.middy = middy;
644
+ exports.offlineAuth = offlineAuth;
744
645
  exports.promiseWithCache = promiseWithCache;
745
646
  exports.promiseWithTimeout = promiseWithTimeout;
746
647
  exports.requestContextValidator = requestContextValidator;
648
+ exports.responseWrapper = responseWrapper;
747
649
  exports.setupEnvConfig = setupEnvConfig;
748
- exports.sqsEventWrapper = sqsEventWrapper$1;
749
- exports.sqsEventWrapperWithReturn = sqsEventWrapper;
750
650
  exports.urlEncoded = urlEncoded;
751
651
  exports.validateEvent = validateEvent;
752
- exports.validatorMiddleware = validatorMiddleware;
652
+ exports.validator = validator;
753
653
  exports.warmupMiddleware = warmupMiddleware;
@@ -1,12 +1,11 @@
1
- export declare const contextualLogger: () => {
2
- before: ({ event, context }: {
3
- event: any;
4
- context: any;
5
- }) => Promise<void>;
6
- };
7
- export declare const contextualLoggerMiddleware: {
1
+ import { HapnLogger } from '@spytecgps/sdk-logger';
2
+ interface ContextualLoggerOptions {
3
+ logger: HapnLogger;
4
+ }
5
+ export declare const contextualLogger: ({ logger }: ContextualLoggerOptions) => {
8
6
  before: ({ event, context }: {
9
7
  event: any;
10
8
  context: any;
11
9
  }) => Promise<void>;
12
10
  };
11
+ export {};
@@ -0,0 +1,8 @@
1
+ import { APIGatewayProxyResult } from 'aws-lambda';
2
+ import { HandlerResponse } from '../../types';
3
+ export declare const buildResponseBody: <T>(statusCode: number, message: string, data?: T) => {
4
+ success: boolean;
5
+ message: string;
6
+ result: T | undefined;
7
+ };
8
+ export declare const buildProxyResult: <R>({ statusCode, message, data, headers, multiValueHeaders, rawResult, stringifyBody, }: HandlerResponse<R>) => APIGatewayProxyResult;
@@ -1,25 +1,32 @@
1
- import rawMiddy from '@middy/core';
1
+ import middy from '@middy/core';
2
2
  import httpErrorHandler from '@middy/http-error-handler';
3
3
  import httpResponseSerializer from '@middy/http-response-serializer';
4
4
  import sqsJsonBodyParser from '@middy/sqs-json-body-parser';
5
- import { Context as LambdaContext } from 'aws-lambda/handler';
6
- import { MiddyInputHandler } from './types';
7
- import { validatorMiddleware } from './validation';
5
+ import { HapnLogger } from '@spytecgps/sdk-logger';
6
+ import { contextualLogger } from './contextualLogger';
7
+ import { ioLogger } from './ioLogger';
8
+ import { offlineAuth } from './offlineAuthorizer';
9
+ import { responseWrapper } from './responseWrapper';
10
+ import { validator } from './validation';
8
11
  import { warmupMiddleware } from './warmup';
9
- declare const baseMiddlewares: ({
12
+ interface BaseMiddlewaresOptions {
13
+ logger: HapnLogger;
14
+ }
15
+ interface ApiGatewayMiddlewaresOptions {
16
+ logger: HapnLogger;
17
+ }
18
+ declare const getBaseMiddlewares: ({ logger }: BaseMiddlewaresOptions) => ({
10
19
  before: ({ event, context }: {
11
20
  event: any;
12
21
  context: any;
13
22
  }) => Promise<void>;
14
- } | rawMiddy.MiddlewareObj<any, any, Error, LambdaContext> | {
23
+ } | middy.MiddlewareObj<any, any, Error, import("aws-lambda").Context> | {
15
24
  before: (request: any) => string;
16
25
  })[];
17
- declare const apiGatewayMiddlewares: (rawMiddy.MiddlewareObj<any, any, Error, LambdaContext> | {
26
+ declare const getApiGatewayMiddlewares: ({ logger }: ApiGatewayMiddlewaresOptions) => (middy.MiddlewareObj<any, any, Error, import("aws-lambda").Context> | {
18
27
  before: (request: any) => Promise<void>;
19
28
  } | {
20
29
  after: (req: any) => void;
21
30
  onError: (req: any) => void;
22
31
  })[];
23
- declare const middy: <TEvent, TResult, TContext extends LambdaContext>(handler: MiddyInputHandler<TEvent, TResult, TContext>) => rawMiddy.MiddyfiedHandler<TEvent, TResult, Error, TContext>;
24
- declare const apiGatewayMiddy: <TEvent, TResult, TContext extends LambdaContext>(handler: MiddyInputHandler<TEvent, TResult, TContext>) => rawMiddy.MiddyfiedHandler<TEvent, TResult, Error, TContext>;
25
- export { apiGatewayMiddlewares, apiGatewayMiddy, baseMiddlewares, httpErrorHandler, httpResponseSerializer, middy, sqsJsonBodyParser, validatorMiddleware, warmupMiddleware, };
32
+ export { contextualLogger, getApiGatewayMiddlewares, getBaseMiddlewares, httpErrorHandler, httpResponseSerializer, ioLogger, middy, offlineAuth, responseWrapper, sqsJsonBodyParser, validator, warmupMiddleware, };
@@ -1 +1,6 @@
1
- export declare const ioLoggerMiddleware: import("@middy/core").MiddlewareObj<any, any, Error, import("aws-lambda").Context>;
1
+ import { HapnLogger } from '@spytecgps/sdk-logger';
2
+ interface IoLoggerOptions {
3
+ logger: HapnLogger;
4
+ }
5
+ export declare const ioLogger: ({ logger }: IoLoggerOptions) => import("@middy/core").MiddlewareObj<any, any, Error, import("aws-lambda").Context>;
6
+ export {};
@@ -1,8 +1,10 @@
1
- interface OfflineMiddlewareOptions {
1
+ import { HapnLogger } from '@spytecgps/sdk-logger';
2
+ interface OfflineAuthOptions {
2
3
  authFunctionName?: string;
3
4
  enabled?: boolean;
5
+ logger: HapnLogger;
4
6
  }
5
- export declare const offlineAuthMiddleware: ({ authFunctionName, enabled, }?: OfflineMiddlewareOptions) => {
7
+ export declare const offlineAuth: ({ authFunctionName, enabled, logger, }: OfflineAuthOptions) => {
6
8
  before: (request: any) => Promise<void>;
7
9
  };
8
10
  export {};
@@ -1,4 +1,9 @@
1
- export declare const responseWrapperMiddleware: () => {
1
+ import { HapnLogger } from '@spytecgps/sdk-logger';
2
+ interface ResponseWrapperOptions {
3
+ logger: HapnLogger;
4
+ }
5
+ export declare const responseWrapper: ({ logger }: ResponseWrapperOptions) => {
2
6
  after: (req: any) => void;
3
7
  onError: (req: any) => void;
4
8
  };
9
+ export {};
@@ -1,9 +1,11 @@
1
+ import { HapnLogger } from '@spytecgps/sdk-logger';
1
2
  import Joi from 'joi';
2
- interface ValidatorOpts {
3
+ interface ValidatorOptions {
3
4
  schema: Joi.Schema;
4
5
  allowUnknown?: boolean;
6
+ logger: HapnLogger;
5
7
  }
6
- export declare const validatorMiddleware: ({ schema, allowUnknown }: ValidatorOpts) => {
8
+ export declare const validator: ({ schema, allowUnknown, logger }: ValidatorOptions) => {
7
9
  before: (request: any) => void;
8
10
  };
9
11
  export {};
@@ -1,3 +1,6 @@
1
1
  import { ObjectSchema, ValidationOptions } from 'joi';
2
+ /**
3
+ * @deprecated
4
+ */
2
5
  declare const validateEvent: <T>(event: any, schema?: ObjectSchema<T>, validateOptions?: ValidationOptions) => T;
3
6
  export default validateEvent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spytecgps/lambda-utils",
3
- "version": "2.3.28",
3
+ "version": "3.0.0",
4
4
  "description": "Lambda Utils",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -39,7 +39,7 @@
39
39
  "qs": "^6.10.1"
40
40
  },
41
41
  "peerDependencies": {
42
- "@spytecgps/sdk-logger": "^2.0.8"
42
+ "@spytecgps/sdk-logger": "^2.0.18"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@aws-sdk/client-lambda": "^3.731.1",
@@ -48,7 +48,7 @@
48
48
  "@rollup/plugin-node-resolve": "^16.0.0",
49
49
  "@rollup/plugin-terser": "^0.4.4",
50
50
  "@rollup/plugin-typescript": "^12.1.2",
51
- "@spytecgps/sdk-logger": "^2.0.8",
51
+ "@spytecgps/sdk-logger": "^2.0.18",
52
52
  "@types/aws-lambda": "^8.10.76",
53
53
  "@types/jest": "^29.5.12",
54
54
  "@types/joi": "^17.2.3",