@comicrelief/lambda-wrapper 1.10.1 → 2.0.0-beta.2

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 (89) hide show
  1. package/README.md +172 -47
  2. package/dist/core/DependencyAwareClass.d.ts +15 -0
  3. package/dist/core/DependencyAwareClass.d.ts.map +1 -0
  4. package/dist/core/DependencyAwareClass.js +20 -0
  5. package/dist/core/DependencyAwareClass.js.map +1 -0
  6. package/dist/core/DependencyInjection.d.ts +62 -0
  7. package/dist/core/DependencyInjection.d.ts.map +1 -0
  8. package/dist/core/DependencyInjection.js +83 -0
  9. package/dist/core/DependencyInjection.js.map +1 -0
  10. package/dist/core/LambdaWrapper.d.ts +61 -0
  11. package/dist/core/LambdaWrapper.d.ts.map +1 -0
  12. package/dist/core/LambdaWrapper.js +126 -0
  13. package/dist/core/LambdaWrapper.js.map +1 -0
  14. package/dist/core/config.d.ts +20 -0
  15. package/dist/core/config.d.ts.map +1 -0
  16. package/dist/core/config.js +21 -0
  17. package/dist/core/config.js.map +1 -0
  18. package/dist/index.d.ts +28 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +59 -162
  21. package/dist/index.js.map +1 -0
  22. package/dist/models/ResponseModel.d.ts +91 -0
  23. package/dist/models/ResponseModel.d.ts.map +1 -0
  24. package/dist/models/ResponseModel.js +100 -0
  25. package/dist/models/ResponseModel.js.map +1 -0
  26. package/dist/models/SQSMessageModel.d.ts +46 -0
  27. package/dist/models/SQSMessageModel.d.ts.map +1 -0
  28. package/dist/models/SQSMessageModel.js +65 -0
  29. package/dist/models/SQSMessageModel.js.map +1 -0
  30. package/dist/models/StatusModel.d.ts +40 -0
  31. package/dist/models/StatusModel.d.ts.map +1 -0
  32. package/dist/models/StatusModel.js +50 -0
  33. package/dist/models/StatusModel.js.map +1 -0
  34. package/dist/services/BaseConfigService.d.ts +101 -0
  35. package/dist/services/BaseConfigService.d.ts.map +1 -0
  36. package/dist/services/BaseConfigService.js +180 -0
  37. package/dist/services/BaseConfigService.js.map +1 -0
  38. package/dist/services/HTTPService.d.ts +29 -0
  39. package/dist/services/HTTPService.d.ts.map +1 -0
  40. package/dist/services/HTTPService.js +55 -0
  41. package/dist/services/HTTPService.js.map +1 -0
  42. package/dist/services/LoggerService.d.ts +102 -0
  43. package/dist/services/LoggerService.d.ts.map +1 -0
  44. package/dist/services/LoggerService.js +258 -0
  45. package/dist/services/LoggerService.js.map +1 -0
  46. package/dist/services/RequestService.d.ts +119 -0
  47. package/dist/services/RequestService.d.ts.map +1 -0
  48. package/dist/services/RequestService.js +293 -0
  49. package/dist/services/RequestService.js.map +1 -0
  50. package/dist/services/SQSService.d.ts +181 -0
  51. package/dist/services/SQSService.d.ts.map +1 -0
  52. package/dist/services/SQSService.js +366 -0
  53. package/dist/services/SQSService.js.map +1 -0
  54. package/dist/services/TimerService.d.ts +22 -0
  55. package/dist/services/TimerService.d.ts.map +1 -0
  56. package/dist/services/TimerService.js +40 -0
  57. package/dist/services/TimerService.js.map +1 -0
  58. package/dist/utils/LambdaTermination.d.ts +14 -0
  59. package/dist/utils/LambdaTermination.d.ts.map +1 -0
  60. package/dist/utils/LambdaTermination.js +23 -0
  61. package/dist/utils/LambdaTermination.js.map +1 -0
  62. package/dist/utils/PromisifiedDelay.d.ts +17 -0
  63. package/dist/utils/PromisifiedDelay.d.ts.map +1 -0
  64. package/dist/utils/PromisifiedDelay.js +48 -0
  65. package/dist/utils/PromisifiedDelay.js.map +1 -0
  66. package/package.json +29 -19
  67. package/.eslintrc.yml +0 -16
  68. package/.github/dependabot.yml +0 -11
  69. package/.github/workflows/main.yml +0 -74
  70. package/babel.config.js +0 -13
  71. package/dist/Config/Dependencies.js +0 -40
  72. package/dist/DependencyInjection/DependencyAware.class.js +0 -47
  73. package/dist/DependencyInjection/DependencyInjection.class.js +0 -131
  74. package/dist/Model/CloudEvent.model.js +0 -156
  75. package/dist/Model/Model.model.js +0 -45
  76. package/dist/Model/Response.model.js +0 -150
  77. package/dist/Model/SQS/MarketingPreference.constraints.json +0 -28
  78. package/dist/Model/SQS/MarketingPreference.model.js +0 -641
  79. package/dist/Model/SQS/Message.model.js +0 -104
  80. package/dist/Model/Status.model.js +0 -82
  81. package/dist/Service/BaseConfig.service.js +0 -225
  82. package/dist/Service/HTTP.service.js +0 -70
  83. package/dist/Service/Logger.service.js +0 -293
  84. package/dist/Service/Request.service.js +0 -341
  85. package/dist/Service/SQS.service.js +0 -420
  86. package/dist/Service/Timer.service.js +0 -55
  87. package/dist/Wrapper/LambdaTermination.js +0 -39
  88. package/dist/Wrapper/LambdaWrapper.js +0 -164
  89. package/dist/Wrapper/PromisifiedDelay.js +0 -59
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const Sentry = __importStar(require("@sentry/node"));
30
+ const epsagon_1 = __importDefault(require("epsagon"));
31
+ const winston_1 = __importDefault(require("winston"));
32
+ const DependencyAwareClass_1 = __importDefault(require("../core/DependencyAwareClass"));
33
+ const sentryIsAvailable = typeof process.env.RAVEN_DSN !== 'undefined' && typeof process.env.RAVEN_DSN === 'string' && process.env.RAVEN_DSN !== 'undefined';
34
+ // initialise the Sentry client if available
35
+ if (sentryIsAvailable) {
36
+ Sentry.init({
37
+ dsn: process.env.RAVEN_DSN,
38
+ shutdownTimeout: 5,
39
+ environment: process.env.STAGE,
40
+ });
41
+ }
42
+ /**
43
+ * Provides logging and integrations with our monitoring tools.
44
+ *
45
+ * For logging we use [Winston](https://github.com/winstonjs/winston).
46
+ * Errors will also be sent to [Sentry](https://sentry.io/) and
47
+ * [Epsagon](https://epsagon.com/) if those are available.
48
+ */
49
+ class LoggerService extends DependencyAwareClass_1.default {
50
+ constructor(di) {
51
+ super(di);
52
+ this.sentry = null;
53
+ this.winston = null;
54
+ const { event, context } = this.di;
55
+ if (sentryIsAvailable && !di.isOffline) {
56
+ Sentry.configureScope((scope) => {
57
+ scope.setTags({
58
+ Event: event,
59
+ Context: context,
60
+ });
61
+ scope.setExtras({
62
+ lambda: context.functionName,
63
+ memory_size: context.memoryLimitInMB,
64
+ log_group: context.logGroupName,
65
+ log_stream: context.logStreamName,
66
+ stage: process.env.STAGE,
67
+ path: event.path,
68
+ httpMethod: event.httpMethod,
69
+ });
70
+ });
71
+ this.sentry = Sentry;
72
+ }
73
+ }
74
+ /**
75
+ * Returns a Winston logger configured for our lambdas.
76
+ *
77
+ * Note: If the lambda is executed in a `serverless-offline` context, the
78
+ * log output to console will be pretty-printed.
79
+ */
80
+ getLogger() {
81
+ const loggerFormats = [
82
+ winston_1.default.format.json({
83
+ replacer: (key, value) => {
84
+ if (value instanceof Buffer) {
85
+ return value.toString('base64');
86
+ }
87
+ if (value instanceof Error) {
88
+ return Object.fromEntries(Object.getOwnPropertyNames(value)
89
+ .map((errorKey) => [errorKey, value[errorKey]]));
90
+ }
91
+ return value;
92
+ },
93
+ }),
94
+ ];
95
+ if (this.di.isOffline) {
96
+ loggerFormats.push(winston_1.default.format.prettyPrint());
97
+ }
98
+ return winston_1.default.createLogger({
99
+ level: 'info',
100
+ format: winston_1.default.format.combine(...loggerFormats),
101
+ transports: [new winston_1.default.transports.Console()],
102
+ });
103
+ }
104
+ /**
105
+ * Returns the logger.
106
+ *
107
+ * Uses a cached Winston logger if it has been already created, otherwise it
108
+ * creates one.
109
+ */
110
+ get logger() {
111
+ if (!this.winston) {
112
+ this.winston = this.getLogger();
113
+ }
114
+ return this.winston;
115
+ }
116
+ /**
117
+ * Get Sentry client.
118
+ */
119
+ getSentry() {
120
+ return this.sentry;
121
+ }
122
+ /**
123
+ * While logging an error, we should recognise axios errors and trim down the
124
+ * information to only what is useful for debugging.
125
+ *
126
+ * Keep the following keys:
127
+ * - message.config
128
+ * - message.message
129
+ * - message.response?.status
130
+ * - message.response?.data
131
+ *
132
+ * @param {object} error
133
+ */
134
+ static processAxiosError(error) {
135
+ const processed = {
136
+ config: error.config,
137
+ message: error.message,
138
+ };
139
+ // It's pretty common for axios errors to not have a `response`,
140
+ // for example if there was a network error or timeout.
141
+ if (error.response) {
142
+ processed.response = {
143
+ status: error.response.status,
144
+ data: error.response.data,
145
+ };
146
+ }
147
+ return processed;
148
+ }
149
+ /**
150
+ * Transform the original message before it is passed to the logger.
151
+ *
152
+ * @param message
153
+ */
154
+ static processMessage(message) {
155
+ let processed = message;
156
+ if (processed?.isAxiosError) {
157
+ processed = LoggerService.processAxiosError(processed);
158
+ }
159
+ return processed;
160
+ }
161
+ /**
162
+ * Log Error Message
163
+ *
164
+ * @param error object
165
+ * @param message string
166
+ */
167
+ error(error, message = '') {
168
+ if (sentryIsAvailable && error instanceof Error) {
169
+ Sentry.captureException(error);
170
+ }
171
+ if (typeof process.env.EPSAGON_TOKEN === 'string'
172
+ && process.env.EPSAGON_TOKEN !== 'undefined'
173
+ && typeof process.env.EPSAGON_SERVICE_NAME === 'string'
174
+ && process.env.EPSAGON_SERVICE_NAME !== 'undefined'
175
+ && error instanceof Error) {
176
+ epsagon_1.default.setError(error);
177
+ }
178
+ this.logger.log('error', message, { error: LoggerService.processMessage(error) });
179
+ this.label('error', true);
180
+ this.metric('error', 'error', true);
181
+ }
182
+ /**
183
+ * Log an informational message.
184
+ *
185
+ * @param message
186
+ */
187
+ info(message) {
188
+ this.logger.log('info', LoggerService.processMessage(message));
189
+ }
190
+ /**
191
+ * Log an error, using `LoggerService.error` or `LoggerService.info` based
192
+ * on `process.env.LOGGER_SOFT_WARNING`.
193
+ *
194
+ * Please note that `LoggerService.error` and `LoggerService.info` have
195
+ * different signatures. The function uses the shared argument instead of
196
+ * introducing ambiguity.
197
+ *
198
+ * @param error
199
+ */
200
+ warning(error) {
201
+ const softWarningValues = ['true', '1'];
202
+ if (softWarningValues.includes(process.env.LOGGER_SOFT_WARNING || '')) {
203
+ return this.info(error);
204
+ }
205
+ return this.error(error);
206
+ }
207
+ /**
208
+ * Add a label to the function's Epsagon trace.
209
+ *
210
+ * @param descriptor
211
+ * @param silent If `false`, the label will also be logged. (default: false)
212
+ */
213
+ label(descriptor, silent = false) {
214
+ if (typeof process.env.EPSAGON_TOKEN === 'string'
215
+ && process.env.EPSAGON_TOKEN !== 'undefined'
216
+ && typeof process.env.EPSAGON_SERVICE_NAME === 'string'
217
+ && process.env.EPSAGON_SERVICE_NAME !== 'undefined') {
218
+ epsagon_1.default.label(descriptor, true);
219
+ }
220
+ if (!silent) {
221
+ this.logger.log('info', `label - ${descriptor}`);
222
+ }
223
+ }
224
+ /**
225
+ * Add a metric to the function's Epsagon trace.
226
+ *
227
+ * @param descriptor
228
+ * @param stat
229
+ * @param silent If `false`, the metric will also be logged. (default: false)
230
+ */
231
+ metric(descriptor, stat, silent = false) {
232
+ if (typeof process.env.EPSAGON_TOKEN === 'string'
233
+ && process.env.EPSAGON_TOKEN !== 'undefined'
234
+ && typeof process.env.EPSAGON_SERVICE_NAME === 'string'
235
+ && process.env.EPSAGON_SERVICE_NAME !== 'undefined') {
236
+ epsagon_1.default.label(descriptor, stat);
237
+ }
238
+ if (silent === false) {
239
+ this.logger.log('info', `metric - ${descriptor} - ${stat}`);
240
+ }
241
+ }
242
+ /**
243
+ * Log an object so that it can be inspected.
244
+ *
245
+ * @param action What are we doing with the object, e.g. 'Processing'
246
+ * @param object The object to be stored in logs
247
+ * @param level 'error', 'warning' or 'info'
248
+ */
249
+ object(action, object, level = 'info') {
250
+ if (!(['error', 'warning', 'info'].includes(level))) {
251
+ throw new Error('Unrecognised log level');
252
+ }
253
+ const payload = JSON.stringify(object, null, 4);
254
+ return this[level](`${action}: '${payload}'`);
255
+ }
256
+ }
257
+ exports.default = LoggerService;
258
+ //# sourceMappingURL=LoggerService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoggerService.js","sourceRoot":"","sources":["../../src/services/LoggerService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /// <reference types="node" />
2
+ import { APIGatewayProxyEvent } from 'aws-lambda';
3
+ import DependencyAwareClass from '../core/DependencyAwareClass';
4
+ import ResponseModel from '../models/ResponseModel';
5
+ export declare const REQUEST_TYPES: {
6
+ DELETE: string;
7
+ GET: string;
8
+ HEAD: string;
9
+ OPTIONS: string;
10
+ PATCH: string;
11
+ POST: string;
12
+ PUT: string;
13
+ };
14
+ export declare const HTTP_METHODS_WITHOUT_PAYLOADS: string[];
15
+ export declare const HTTP_METHODS_WITH_PAYLOADS: string[];
16
+ export declare const ERROR_TYPES: {
17
+ VALIDATION_ERROR: ResponseModel;
18
+ };
19
+ export declare type RequestFile = {
20
+ type: string;
21
+ filename: string;
22
+ contentType: string;
23
+ content: string | Buffer;
24
+ };
25
+ /**
26
+ * Provides access to components of the HTTP request being handled.
27
+ */
28
+ export default class RequestService extends DependencyAwareClass {
29
+ /**
30
+ * Get a parameter from the request.
31
+ *
32
+ * @param parameter
33
+ * @param ifNull Value to return if the parameter is not set.
34
+ * @param requestType
35
+ */
36
+ get(parameter: string, ifNull?: string | null, requestType?: string): string | string[] | null;
37
+ /**
38
+ * Get all HTTP headers included in the request.
39
+ *
40
+ * @returns An object with a key for each header.
41
+ */
42
+ getAllHeaders(): {
43
+ [x: string]: string | undefined;
44
+ };
45
+ /**
46
+ * Get an HTTP header from the request.
47
+ *
48
+ * The header name is case-insensitive.
49
+ *
50
+ * @param name The name of the header.
51
+ * @param [whenMissing] Value to return if the header is missing.
52
+ * (default: empty string)
53
+ */
54
+ getHeader(name: string, whenMissing?: string): string;
55
+ /**
56
+ * Get an authorization token from the `Authorization` header.
57
+ */
58
+ getAuthorizationToken(): string | null;
59
+ /**
60
+ * Get a path parameter, or all path parameters if no `parameter` is given.
61
+ *
62
+ * @param parameter
63
+ * @param ifNull Value to return if the parameter is not set.
64
+ */
65
+ getPathParameter(parameter?: string, ifNull?: {}): any;
66
+ /**
67
+ * Get all request parameters
68
+ *
69
+ * @param requestType
70
+ */
71
+ getAll(requestType?: string): any;
72
+ /**
73
+ * Fetch the request IP address
74
+ */
75
+ getIp(): string | null;
76
+ /**
77
+ * Get user agent details from the `User-Agent` header.
78
+ */
79
+ getUserBrowserAndDevice(): {
80
+ 'browser-type': string;
81
+ 'browser-version': string;
82
+ 'device-type': string;
83
+ 'operating-system': string;
84
+ 'operating-system-version': string;
85
+ } | null;
86
+ /**
87
+ * Test a request against validation constraints.
88
+ *
89
+ * See [validate.js](https://validatejs.org/) for how to write constraints.
90
+ *
91
+ * @param constraints
92
+ */
93
+ validateAgainstConstraints(constraints: object): Promise<void>;
94
+ /**
95
+ * Fetch the request multipart form.
96
+ *
97
+ * @param useBuffer Whether to return file content as a `Buffer`.
98
+ */
99
+ parseForm(useBuffer: boolean): Record<string, string | RequestFile>;
100
+ /**
101
+ * Fetch the request AWS event Records
102
+ */
103
+ getAWSRecords(): any;
104
+ /**
105
+ * Gets a value independently from the case of the key.
106
+ *
107
+ * @param object
108
+ * @param key
109
+ */
110
+ static getValueIgnoringKeyCase(object: Record<string, string | undefined>, key: string): string | undefined;
111
+ /**
112
+ * Returns the content type
113
+ * assoiated with the request
114
+ *
115
+ * @param event
116
+ */
117
+ static getBoundary(event: APIGatewayProxyEvent): string | undefined;
118
+ }
119
+ //# sourceMappingURL=RequestService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RequestService.d.ts","sourceRoot":"","sources":["../../src/services/RequestService.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKlD,OAAO,oBAAoB,MAAM,8BAA8B,CAAC;AAChE,OAAO,aAAa,MAAM,yBAAyB,CAAC;AAGpD,eAAO,MAAM,aAAa;;;;;;;;CAQzB,CAAC;AAEF,eAAO,MAAM,6BAA6B,UAKzC,CAAC;AAEF,eAAO,MAAM,0BAA0B,UAItC,CAAC;AAGF,eAAO,MAAM,WAAW;;CAEvB,CAAC;AAEF,oBAAY,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC1B,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,oBAAoB;IAC9D;;;;;;OAMG;IACH,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI;IAU9F;;;;OAIG;IACH,aAAa;;;IAKb;;;;;;;;OAQG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,SAAK,GAAG,MAAM;IAUjD;;OAEG;IACH,qBAAqB,IAAI,MAAM,GAAG,IAAI;IAgBtC;;;;;OAKG;IACH,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,GAAG,GAAG;IAqBtD;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,GAAG;IAyDjC;;OAEG;IACH,KAAK,IAAI,MAAM,GAAG,IAAI;IActB;;OAEG;IACH,uBAAuB;;;;;;;IAuBvB;;;;;;OAMG;IACH,0BAA0B,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB9D;;;;OAIG;IACH,SAAS,CAAC,SAAS,EAAE,OAAO;IAiC5B;;OAEG;IACH,aAAa;IAUb;;;;;OAKG;IACH,MAAM,CAAC,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAM3G;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS;CAGpE"}
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ERROR_TYPES = exports.HTTP_METHODS_WITH_PAYLOADS = exports.HTTP_METHODS_WITHOUT_PAYLOADS = exports.REQUEST_TYPES = void 0;
7
+ const querystring_1 = __importDefault(require("querystring"));
8
+ const useragent_1 = __importDefault(require("useragent"));
9
+ const validate_1 = __importDefault(require("validate.js/validate"));
10
+ const xml2js_1 = __importDefault(require("xml2js"));
11
+ const DependencyAwareClass_1 = __importDefault(require("../core/DependencyAwareClass"));
12
+ const ResponseModel_1 = __importDefault(require("../models/ResponseModel"));
13
+ const LoggerService_1 = __importDefault(require("./LoggerService"));
14
+ exports.REQUEST_TYPES = {
15
+ DELETE: 'DELETE',
16
+ GET: 'GET',
17
+ HEAD: 'HEAD',
18
+ OPTIONS: 'OPTIONS',
19
+ PATCH: 'PATCH',
20
+ POST: 'POST',
21
+ PUT: 'PUT',
22
+ };
23
+ exports.HTTP_METHODS_WITHOUT_PAYLOADS = [
24
+ exports.REQUEST_TYPES.DELETE,
25
+ exports.REQUEST_TYPES.GET,
26
+ exports.REQUEST_TYPES.HEAD,
27
+ exports.REQUEST_TYPES.OPTIONS,
28
+ ];
29
+ exports.HTTP_METHODS_WITH_PAYLOADS = [
30
+ exports.REQUEST_TYPES.PATCH,
31
+ exports.REQUEST_TYPES.POST,
32
+ exports.REQUEST_TYPES.PUT,
33
+ ];
34
+ // Define action specific error types
35
+ exports.ERROR_TYPES = {
36
+ VALIDATION_ERROR: new ResponseModel_1.default({}, 400, 'required fields are missing'),
37
+ };
38
+ /**
39
+ * Provides access to components of the HTTP request being handled.
40
+ */
41
+ class RequestService extends DependencyAwareClass_1.default {
42
+ /**
43
+ * Get a parameter from the request.
44
+ *
45
+ * @param parameter
46
+ * @param ifNull Value to return if the parameter is not set.
47
+ * @param requestType
48
+ */
49
+ get(parameter, ifNull, requestType) {
50
+ const queryParameters = this.getAll(requestType);
51
+ if (queryParameters === null) {
52
+ return ifNull ?? null;
53
+ }
54
+ return queryParameters[parameter] ?? ifNull ?? null;
55
+ }
56
+ /**
57
+ * Get all HTTP headers included in the request.
58
+ *
59
+ * @returns An object with a key for each header.
60
+ */
61
+ getAllHeaders() {
62
+ const event = this.getContainer().getEvent();
63
+ return { ...event.headers };
64
+ }
65
+ /**
66
+ * Get an HTTP header from the request.
67
+ *
68
+ * The header name is case-insensitive.
69
+ *
70
+ * @param name The name of the header.
71
+ * @param [whenMissing] Value to return if the header is missing.
72
+ * (default: empty string)
73
+ */
74
+ getHeader(name, whenMissing = '') {
75
+ const headers = this.getAllHeaders();
76
+ if (!headers) {
77
+ return whenMissing;
78
+ }
79
+ const lowerName = name.toLowerCase();
80
+ const key = Object.keys(headers).find((k) => k.toLowerCase() === lowerName);
81
+ return (key && headers[key]) || whenMissing;
82
+ }
83
+ /**
84
+ * Get an authorization token from the `Authorization` header.
85
+ */
86
+ getAuthorizationToken() {
87
+ const authorization = this.getHeader('Authorization');
88
+ if (!authorization) {
89
+ return null;
90
+ }
91
+ const tokenParts = authorization.split(' ');
92
+ const tokenValue = tokenParts[1];
93
+ if (!(tokenParts[0].toLowerCase() === 'bearer' && tokenValue)) {
94
+ return null;
95
+ }
96
+ return tokenValue;
97
+ }
98
+ /**
99
+ * Get a path parameter, or all path parameters if no `parameter` is given.
100
+ *
101
+ * @param parameter
102
+ * @param ifNull Value to return if the parameter is not set.
103
+ */
104
+ getPathParameter(parameter, ifNull = {}) {
105
+ const event = this.getContainer().getEvent();
106
+ // If no parameter has been requested, return all path parameters
107
+ if (!parameter && typeof event.pathParameters === 'object') {
108
+ return event.pathParameters;
109
+ }
110
+ // If a specifc parameter has been requested, return the parameter if it exists
111
+ if (parameter
112
+ && typeof event.pathParameters === 'object'
113
+ && event.pathParameters !== null
114
+ && typeof event.pathParameters[parameter] !== 'undefined') {
115
+ return event.pathParameters[parameter];
116
+ }
117
+ return ifNull;
118
+ }
119
+ /**
120
+ * Get all request parameters
121
+ *
122
+ * @param requestType
123
+ */
124
+ getAll(requestType) {
125
+ const event = this.getContainer().getEvent();
126
+ if (exports.HTTP_METHODS_WITHOUT_PAYLOADS.includes(event.httpMethod)
127
+ || exports.HTTP_METHODS_WITHOUT_PAYLOADS.includes(requestType || '')) {
128
+ // get simple parameters
129
+ const params = {
130
+ ...event.queryStringParameters,
131
+ };
132
+ // add array parameters as arrays
133
+ if (event.multiValueQueryStringParameters !== null) {
134
+ Object.keys(params)
135
+ .filter((key) => key.endsWith('[]'))
136
+ .forEach((key) => {
137
+ params[key] = event.multiValueQueryStringParameters?.[key];
138
+ });
139
+ }
140
+ return params;
141
+ }
142
+ if (exports.HTTP_METHODS_WITH_PAYLOADS.includes(event.httpMethod)
143
+ || exports.HTTP_METHODS_WITH_PAYLOADS.includes(requestType || '')) {
144
+ const contentType = this.getHeader('Content-Type');
145
+ let queryParameters = {};
146
+ if (contentType.includes('application/x-www-form-urlencoded')) {
147
+ queryParameters = querystring_1.default.parse(event.body);
148
+ }
149
+ if (contentType.includes('application/json')) {
150
+ try {
151
+ queryParameters = JSON.parse(event.body);
152
+ }
153
+ catch {
154
+ queryParameters = {};
155
+ }
156
+ }
157
+ if (contentType.includes('text/xml')) {
158
+ xml2js_1.default.parseString(event.body, (error, result) => {
159
+ queryParameters = error ? {} : result;
160
+ });
161
+ }
162
+ if (contentType.includes('multipart/form-data')) {
163
+ queryParameters = this.parseForm(true);
164
+ }
165
+ return typeof queryParameters !== 'undefined' ? queryParameters : {};
166
+ }
167
+ return null;
168
+ }
169
+ /**
170
+ * Fetch the request IP address
171
+ */
172
+ getIp() {
173
+ const event = this.getContainer().getEvent();
174
+ if (typeof event.requestContext !== 'undefined'
175
+ && typeof event.requestContext.identity !== 'undefined'
176
+ && typeof event.requestContext.identity.sourceIp !== 'undefined') {
177
+ return event.requestContext.identity.sourceIp;
178
+ }
179
+ return null;
180
+ }
181
+ /**
182
+ * Get user agent details from the `User-Agent` header.
183
+ */
184
+ getUserBrowserAndDevice() {
185
+ const userAgent = this.getHeader('user-agent');
186
+ if (!userAgent) {
187
+ return null;
188
+ }
189
+ try {
190
+ const agent = useragent_1.default.parse(userAgent);
191
+ const os = agent.os.toJSON();
192
+ return {
193
+ 'browser-type': agent.family,
194
+ 'browser-version': agent.toVersion(),
195
+ 'device-type': agent.device.family,
196
+ 'operating-system': os.family,
197
+ 'operating-system-version': agent.os.toVersion(),
198
+ };
199
+ }
200
+ catch {
201
+ this.di.get(LoggerService_1.default).label('user-agent-parsing-failed');
202
+ return null;
203
+ }
204
+ }
205
+ /**
206
+ * Test a request against validation constraints.
207
+ *
208
+ * See [validate.js](https://validatejs.org/) for how to write constraints.
209
+ *
210
+ * @param constraints
211
+ */
212
+ validateAgainstConstraints(constraints) {
213
+ const logger = this.di.get(LoggerService_1.default);
214
+ return new Promise((resolve, reject) => {
215
+ const validation = (0, validate_1.default)(this.getAll(), constraints);
216
+ if (typeof validation === 'undefined') {
217
+ resolve();
218
+ }
219
+ else {
220
+ logger.label('request-validation-failed');
221
+ const validationErrorResponse = exports.ERROR_TYPES.VALIDATION_ERROR;
222
+ validationErrorResponse.setBodyVariable('validation_errors', validation);
223
+ reject(validationErrorResponse);
224
+ }
225
+ });
226
+ }
227
+ /**
228
+ * Fetch the request multipart form.
229
+ *
230
+ * @param useBuffer Whether to return file content as a `Buffer`.
231
+ */
232
+ parseForm(useBuffer) {
233
+ // todo: rewrite this to use a dedicated package and add error handling
234
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
235
+ const event = this.getContainer().getEvent();
236
+ const boundary = RequestService.getBoundary(event);
237
+ const body = event.isBase64Encoded
238
+ ? Buffer.from(event.body, 'base64').toString('binary').trim()
239
+ : event.body;
240
+ const result = {};
241
+ body.split(boundary).forEach((item) => {
242
+ if (/filename=".+"/g.test(item)) {
243
+ const name = item.match(/name=".+";/g)[0].slice(6, -2);
244
+ result[name] = {
245
+ type: 'file',
246
+ filename: item.match(/filename=".+"/g)[0].slice(10, -1),
247
+ contentType: item.match(/Content-Type:\s.+/g)[0].slice(14),
248
+ content: useBuffer
249
+ ? Buffer.from(item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4), 'binary')
250
+ : item.slice(item.search(/Content-Type:\s.+/g) + item.match(/Content-Type:\s.+/g)[0].length + 4, -4),
251
+ };
252
+ }
253
+ else if (/name=".+"/g.test(item)) {
254
+ result[item.match(/name=".+"/g)[0].slice(6, -1)] = item.slice(item.search(/name=".+"/g) + item.match(/name=".+"/g)[0].length + 4, -4);
255
+ }
256
+ });
257
+ /* eslint-enable @typescript-eslint/no-non-null-assertion */
258
+ return result;
259
+ }
260
+ /**
261
+ * Fetch the request AWS event Records
262
+ */
263
+ getAWSRecords() {
264
+ const event = this.getContainer().getEvent();
265
+ const eventRecord = event.Records && event.Records[0];
266
+ if (typeof event.Records !== 'undefined' && typeof event.Records[0] !== 'undefined' && typeof eventRecord.eventSource !== 'undefined') {
267
+ return eventRecord;
268
+ }
269
+ return null;
270
+ }
271
+ /**
272
+ * Gets a value independently from the case of the key.
273
+ *
274
+ * @param object
275
+ * @param key
276
+ */
277
+ static getValueIgnoringKeyCase(object, key) {
278
+ const foundKey = Object.keys(object)
279
+ .find((currentKey) => currentKey.toLocaleLowerCase() === key.toLowerCase());
280
+ return foundKey && object[foundKey];
281
+ }
282
+ /**
283
+ * Returns the content type
284
+ * assoiated with the request
285
+ *
286
+ * @param event
287
+ */
288
+ static getBoundary(event) {
289
+ return this.getValueIgnoringKeyCase(event.headers, 'Content-Type')?.split('=')?.[1];
290
+ }
291
+ }
292
+ exports.default = RequestService;
293
+ //# sourceMappingURL=RequestService.js.map