@comicrelief/lambda-wrapper 1.10.2 → 2.0.0-beta.10

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 +196 -50
  2. package/dist/core/DependencyAwareClass.d.ts +16 -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 +105 -0
  9. package/dist/core/DependencyInjection.js.map +1 -0
  10. package/dist/core/LambdaWrapper.d.ts +73 -0
  11. package/dist/core/LambdaWrapper.d.ts.map +1 -0
  12. package/dist/core/LambdaWrapper.js +170 -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 +57 -143
  21. package/dist/index.js.map +1 -0
  22. package/dist/models/ResponseModel.d.ts +73 -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/services/BaseConfigService.d.ts +101 -0
  31. package/dist/services/BaseConfigService.d.ts.map +1 -0
  32. package/dist/services/BaseConfigService.js +180 -0
  33. package/dist/services/BaseConfigService.js.map +1 -0
  34. package/dist/services/HTTPService.d.ts +29 -0
  35. package/dist/services/HTTPService.d.ts.map +1 -0
  36. package/dist/services/HTTPService.js +55 -0
  37. package/dist/services/HTTPService.js.map +1 -0
  38. package/dist/services/LoggerService.d.ts +102 -0
  39. package/dist/services/LoggerService.d.ts.map +1 -0
  40. package/dist/services/LoggerService.js +249 -0
  41. package/dist/services/LoggerService.js.map +1 -0
  42. package/dist/services/RequestService.d.ts +119 -0
  43. package/dist/services/RequestService.d.ts.map +1 -0
  44. package/dist/services/RequestService.js +293 -0
  45. package/dist/services/RequestService.js.map +1 -0
  46. package/dist/services/SQSService.d.ts +263 -0
  47. package/dist/services/SQSService.d.ts.map +1 -0
  48. package/dist/services/SQSService.js +411 -0
  49. package/dist/services/SQSService.js.map +1 -0
  50. package/dist/services/TimerService.d.ts +22 -0
  51. package/dist/services/TimerService.d.ts.map +1 -0
  52. package/dist/services/TimerService.js +40 -0
  53. package/dist/services/TimerService.js.map +1 -0
  54. package/dist/types/Status.d.ts +6 -0
  55. package/dist/types/Status.d.ts.map +1 -0
  56. package/dist/types/Status.js +3 -0
  57. package/dist/types/Status.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 +36 -26
  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 -33
  72. package/dist/DependencyInjection/DependencyAware.class.js +0 -40
  73. package/dist/DependencyInjection/DependencyInjection.class.js +0 -115
  74. package/dist/Model/CloudEvent.model.js +0 -138
  75. package/dist/Model/Model.model.js +0 -39
  76. package/dist/Model/Response.model.js +0 -135
  77. package/dist/Model/SQS/MarketingPreference.constraints.json +0 -28
  78. package/dist/Model/SQS/MarketingPreference.model.js +0 -583
  79. package/dist/Model/SQS/Message.model.js +0 -92
  80. package/dist/Model/Status.model.js +0 -71
  81. package/dist/Service/BaseConfig.service.js +0 -204
  82. package/dist/Service/HTTP.service.js +0 -57
  83. package/dist/Service/Logger.service.js +0 -256
  84. package/dist/Service/Request.service.js +0 -294
  85. package/dist/Service/SQS.service.js +0 -367
  86. package/dist/Service/Timer.service.js +0 -46
  87. package/dist/Wrapper/LambdaTermination.js +0 -34
  88. package/dist/Wrapper/LambdaWrapper.js +0 -143
  89. package/dist/Wrapper/PromisifiedDelay.js +0 -55
package/README.md CHANGED
@@ -4,97 +4,243 @@
4
4
  [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
5
5
  [![semantic-release](https://badge.fury.io/js/%40comicrelief%2Flambda-wrapper.svg)](https://www.npmjs.com/package/@comicrelief/lambda-wrapper)
6
6
 
7
- When writing Serverless endpoints, we have found ourselves replicating a lot of boiler plate code to do basic actions, such as reading request variables or writing to SQS. The aim of this package is to provide a wrapper for our Lambda functions, to provide some level of dependency and configuration injection and to reduce time spent on project setup.
7
+ When writing Serverless applications, we have found ourselves replicating a lot of boilerplate code to do basic actions, such as reading request data or sending messages to SQS. The aim of this package is to provide a wrapper for our Lambda functions, to provide some level of dependency and configuration injection and to reduce time spent on project setup.
8
8
 
9
- > 🚀 [Lambda Wrapper v2 is now available in beta!](https://github.com/comicrelief/lambda-wrapper/tree/beta) This major release includes TypeScript support and some significant design changes.
9
+ If you're coming from v1 and updating to v2, check out the [v2 migration guide](docs/migration/v2.md).
10
10
 
11
- ## Installation & usage
11
+ ## Getting started
12
12
 
13
- Install via npm:
13
+ Install via npm or Yarn:
14
14
 
15
15
  ```bash
16
- npm install --save @comicrelief/lambda-wrapper
16
+ npm i @comicrelief/lambda-wrapper@beta
17
+ # or
18
+ yarn add @comicrelief/lambda-wrapper@beta
17
19
  ```
18
20
 
19
- Or via yarn:
21
+ You can then wrap your Lambda handler functions like this:
20
22
 
21
- ```bash
22
- yarn add @comicrelief/lambda-wrapper
23
- ```
24
-
25
- You can then wrap your lambdas as follows.
26
-
27
- ```js
28
- import {
29
- LambdaWrapper,
23
+ ```ts
24
+ // src/action/Hello.ts
25
+ import lambdaWrapper, {
30
26
  ResponseModel,
31
27
  RequestService,
32
28
  } from '@comicrelief/lambda-wrapper';
33
29
 
34
- export default LambdaWrapper({}, (di, request, done) => {
35
- const response = new ResponseModel({}, 200, `hello ${request.get('name', 'nobody')}`);
36
- done(null, response.generate());
30
+ export default lambdaWrapper.wrap(async (di) => {
31
+ const request = di.get(RequestService);
32
+ return ResponseModel.generate(
33
+ {},
34
+ 200,
35
+ `hello ${request.get('name', 'nobody')}`,
36
+ );
37
37
  });
38
38
  ```
39
39
 
40
- ## Serverless Offline & SQS Emulation
40
+ Here we've used the default export `lambdaWrapper` which is a preconfigured instance that can be used out of the box. You'll likely want to add your own dependencies and service config using the `configure` method:
41
+
42
+ ```ts
43
+ // src/config/LambdaWrapper.ts
44
+ import lambdaWrapper from '@comicrelief/lambda-wrapper';
45
+
46
+ export default lambdaWrapper.configure({
47
+ // your config goes here
48
+ });
49
+ ```
41
50
 
42
- Serverless Offline only emulates API Gateway and Lambda, so publishing an SQS message would use the real SQS queue and trigger the consumer function (if any) in AWS. When working with offline code, you often want the local functions to be invoked instead.
51
+ `configure` returns a new Lambda Wrapper instance with the given configuration. You'll want to export it and then use this when wrapping your handler functions.
43
52
 
44
- Offline SQS behaviour can be configured by setting the `LAMBDA_WRAPPER_OFFLINE_SQS_MODE` environment variable. Available modes are:
53
+ Read the next section to see what goes inside the config object!
45
54
 
46
- - `direct` (the default): invokes the consumer function directly via an offline Lambda endpoint
47
- - `local`: send messages to an offline SQS endpoint, such as Localstack
48
- - `aws`: no special handling of SQS offline; messages will be sent to AWS
55
+ If you want to start from scratch without the built-in dependencies, you can use the `LambdaWrapper` constructor directly.
49
56
 
50
- Details of each mode are documented in the sections below. When you send a message using `SQSService.prototype.publish`, it will check which mode to use and dispatch the message appropriately. These modes take effect only when running offline (as defined by `DependencyInjection.prototype.isOffline`). In a deployed environment, SQS messages will always be sent to AWS SQS.
57
+ ```ts
58
+ // src/config/LambdaWrapper.ts
59
+ import { LambdaWrapper } from '@comicrelief/lambda-wrapper';
51
60
 
52
- ### Direct Lambda mode
61
+ export default new LambdaWrapper({
62
+ // your config goes here
63
+ });
64
+ ```
53
65
 
54
- This is the default mode if `LAMBDA_WRAPPER_OFFLINE_SQS_MODE` is not set. A Lambda client will be created and the message will be delivered to the offline Lambda endpoint, effectively running the consumer function _immediately_ as part of the original Lambda invocation. This works very well in the offline environment because invoking a Lambda function will trigger its whole (local) execution tree.
66
+ ## Dependencies
55
67
 
56
- To take advantage of SQS emulation, you will need to define the following in the implementing service:
68
+ Lambda Wrapper comes with some commonly used dependencies built in:
57
69
 
58
- **QUEUE_CONSUMERS**
70
+ - [HTTPService](docs/services/HTTPService.md)
71
+ - [LoggerService](docs/services/LoggerService.md)
72
+ - [RequestService](docs/services/RequestService.md)
73
+ - [SQSService](docs/services/SQSService.md)
74
+ - [TimerService](docs/services/TimerService.md)
59
75
 
60
- In your `src/Config/Configuration` define a `QUEUE_CONSUMERS` object. `QUEUE_CONSUMERS` will map the queue name to the fully qualified `FunctionName` that we want to trigger when messages are published to that queue.
76
+ Access these via dependency injection. You've already seen an example of this where we got `RequestService`. Pass the dependency class to `di.get()` to get its instance:
61
77
 
62
- You will need to export `QUEUE_CONSUMERS` as part of your default export, alongside `DEFINITIONS`, `DEPENDENCIES`, `QUEUES`, `QUEUE_DEFINITIONS`, etc.
78
+ ```ts
79
+ export default lambdaWrapper.wrap(async (di) => {
80
+ const request = di.get(RequestService);
81
+ const sqs = di.get(SQSService);
82
+ // ...
83
+ });
84
+ ```
63
85
 
64
- A `Configuration` example can be found in the `serverless-prize-platform` repository [here](https://github.com/comicrelief/serverless-prize-platform/blob/master/src/Config/Configuration.js).
86
+ To add your own dependencies, first extend `DependencyAwareClass`.
65
87
 
66
- **process.env.SERVICE_LAMBDA_URL**
88
+ ```ts
89
+ // src/services/MyService.ts
90
+ import { DependencyAwareClass } from '@comicrelief/lambda-wrapper';
67
91
 
68
- While creating the Lambda client, we need to point it to our offline environment. LambdaWrapper will take care of the specifics, but it will need to know the Lambda endpoint URL. This _can_ and _must_ be specified via the `SERVICE_LAMBDA_URL` environment variable.
92
+ export default class MyService extends DependencyAwareClass {
93
+ doSomething() {
94
+ // ...
95
+ }
96
+ }
97
+ ```
69
98
 
70
- The URL is likely to be your localhost URL and the next available port from the offline API Gateway. So, if you are running Serverless Offline on `http://localhost:3001`, the Lambda URL is likely to be `http://localhost:3002`. You can check the port in the output during Serverless Offline startup by looking for the following line:
99
+ Then add it to your Lambda Wrapper configuration in the `dependencies` key.
71
100
 
72
- offline: Offline [http for lambda] listening on http://localhost:3002
101
+ ```ts
102
+ // src/config/LambdaWrapper.ts
103
+ import lambdaWrapper from '@comicrelief/lambda-wrapper';
73
104
 
74
- #### Caveats
105
+ import MyService from '@/src/services/MyService';
106
+
107
+ export default lambdaWrapper.configure({
108
+ dependencies: {
109
+ MyService,
110
+ },
111
+ });
112
+ ```
113
+
114
+ Now you can use it inside your handler functions and other dependencies!
115
+
116
+ ```ts
117
+ // src/action/DoSomething.ts
118
+ import lambdaWrapper from '@/src/config/LambdaWrapper';
119
+ import MyService from '@/src/services/MyService';
120
+
121
+ export default lambdaWrapper.wrap(async (di) => {
122
+ di.get(MyService).doSomething();
123
+ });
124
+ ```
125
+
126
+ ## Service config
127
+
128
+ Some dependencies need their own config. This goes in per-service keys within your Lambda Wrapper config. For an example, see [SQSService](docs/services/SQSService.md) which uses the `sqs` key.
129
+
130
+ ```ts
131
+ export default lambdaWrapper.configure({
132
+ dependencies: {
133
+ // your dependencies
134
+ },
135
+ sqs: {
136
+ // your SQSService config
137
+ },
138
+ // ... other configs ...
139
+ });
140
+ ```
75
141
 
76
- 1. You will be running the SQS-triggered lambdas in the same Serverless Offline context as your triggering lambda. Expect logs from both lambdas in the Serverless Offline output.
142
+ To use config with your own dependencies, you need to do three things:
77
143
 
78
- 2. If you await `sqs.publish` you will effectively wait until all SQS-triggered lambdas (and possibly their own SQS-triggered lambdas) have all completed. This is necessary to avoid any pending execution (i.e. the lambda terminating before its async processes are completed).
144
+ 1. Define the key and type of your config object.
79
145
 
80
- 3. If the triggered lambda incurs an exception, this will be propagated upstream, effectively killing the execution of the calling lambda.
146
+ Using `SQSService` as an example, we have the `sqs` key which has the `SQSServiceConfig` type:
147
+
148
+ ```ts
149
+ export interface SQSServiceConfig {
150
+ queues?: Record<string, string>;
151
+ queueConsumers?: Record<string, string>;
152
+ }
153
+ ```
154
+
155
+ 2. Define a type that can be applied to a Lambda Wrapper config.
156
+
157
+ This simply combines the key and type defined in step 1. Conventionally we name these `With...` types.
158
+
159
+ ```ts
160
+ export interface WithSQSServiceConfig {
161
+ sqs?: SQSServiceConfig;
162
+ }
163
+ ```
164
+
165
+ In the case of `SQSService`, the `sqs` key is optional because this dependency is included by default and not all applications need it. If your dependency requires config in order to work, you can make this a required key.
166
+
167
+ 3. In your dependency constructor, cast the config to this type.
168
+
169
+ ```ts
170
+ export default class SQSService extends DependencyAwareClass {
171
+ constructor(di: DependencyInjection) {
172
+ super(di);
173
+
174
+ const config = (this.di.config as WithSQSServiceConfig).sqs;
175
+ // Bear in mind that because the `sqs` key is optional, the type of
176
+ // `config` will be `SQSServiceConfig | undefined`. Take care when
177
+ // accessing its properties! You can use optional chaining:
178
+ const queues = config?.queues || {};
179
+ // ...
180
+ }
181
+ }
182
+ ```
183
+
184
+ When you go to configure your Lambda Wrapper, you can now include your dependency's config type in the generic for `configure` to get IntelliSense completions and type checking for your config keys.
185
+
186
+ ```ts
187
+ lambdaWrapper.configure<WithSQSServiceConfig>({
188
+ sqs: {
189
+ queues: 42 // Oops! This will be flagged as a type error by TypeScript
190
+ },
191
+ });
192
+ ```
193
+
194
+ You can combine types for multiple dependencies if needed using `&`:
195
+
196
+ ```ts
197
+ lambdaWrapper.configure<WithSQSServiceConfig & WithOtherServiceConfig>({
198
+ sqs: {
199
+ // SQSService config
200
+ },
201
+ other: {
202
+ // OtherService config
203
+ },
204
+ });
205
+ ```
206
+
207
+ ## Monitoring
208
+
209
+ At Comic Relief we use [Lumigo](https://lumigo.io/) for monitoring and observability of our deployed services. Lambda Wrapper includes the Lumigo tracer to allow us to tag traces with custom labels and metrics ([execution tags](https://docs.lumigo.io/docs/execution-tags)).
210
+
211
+ Lumigo integration works out-of-the-box with Lumigo's [auto-trace feature](https://docs.lumigo.io/docs/serverless-applications#automatic-instrumentation). If you prefer manual tracing, enable it by setting `LUMIGO_TRACER_TOKEN` in your Lambda environment variables.
212
+
213
+ And if you don't use Lumigo, don't worry, their tracer will not be instantiated in your functions and no calls will be made to their servers unless `LUMIGO_TRACER_TOKEN` is set.
214
+
215
+ ## Notes
216
+
217
+ Lambda Wrapper's dependency injection relies on class names being preserved. If your build process includes minifying or uglifying your code, you'll need to disable these transformations.
218
+
219
+ In many of our projects we use `serverless-webpack` to bundle service code prior to deployment. To disable name mangling, set `optimization.minimize` to `false` in your webpack config:
220
+
221
+ ```js
222
+ // webpack.config.js
223
+ module.exports = {
224
+ // ...
225
+ optimization: {
226
+ minimize: false,
227
+ },
228
+ ```
81
229
 
82
- ### Local SQS mode
230
+ ## Development
83
231
 
84
- Use this mode by setting `LAMBDA_WRAPPER_OFFLINE_SQS_MODE=local`. Messages will still be sent to an SQS queue, but using a locally simulated version instead of AWS. This allows you to test your service using a tool like Localstack.
232
+ ### Testing
85
233
 
86
- By default, messages will be sent to a SQS service running on `localhost:4576`. If you need to change the hostname, you can set `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_HOST`.
87
- Also, if you need to change the port, you can set `process.env.LAMBDA_WRAPPER_OFFLINE_SQS_PORT`.
234
+ Run `yarn test` to run the unit tests.
88
235
 
89
- ### AWS SQS mode
236
+ When writing a bugfix, start by writing a test that reproduces the problem. It should fail with the current version of Lambda Wrapper, and pass once you've implemented the fix.
90
237
 
91
- Use this mode by setting `LAMBDA_WRAPPER_OFFLINE_SQS_MODE=aws`. Messages will be sent to the real queue in AWS. This mode is useful when a queue is consumed by an external service, rather than another function in the service under test.
238
+ When adding a feature, ensure it's covered by tests that adequately define its behaviour.
92
239
 
93
- In order for queue URLs to be correctly constructed, you must either:
240
+ ### Linting
94
241
 
95
- - set `AWS_ACCOUNT_ID` to the account ID that hosts your queue; or
96
- - invoke offline functions via the Lambda API, passing a context that contains a realistic `invokedFunctionArn` including the account ID.
242
+ Run `yarn lint` to check code style complies to our standard. Many problems can be auto-fixed using `yarn lint --fix`.
97
243
 
98
- ## Semantic release
244
+ ### Releases
99
245
 
100
246
  Release management is automated using [semantic-release](https://www.npmjs.com/package/semantic-release).
@@ -0,0 +1,16 @@
1
+ import DependencyInjection from './DependencyInjection';
2
+ import { LambdaWrapperConfig } from './config';
3
+ /**
4
+ * Base class for dependencies.
5
+ */
6
+ export default class DependencyAwareClass<TConfig extends LambdaWrapperConfig = any> {
7
+ readonly di: DependencyInjection<TConfig>;
8
+ constructor(di: DependencyInjection<TConfig>);
9
+ /**
10
+ * Get dependency injection container.
11
+ *
12
+ * @deprecated Use `this.di` instead.
13
+ */
14
+ getContainer(): DependencyInjection;
15
+ }
16
+ //# sourceMappingURL=DependencyAwareClass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DependencyAwareClass.d.ts","sourceRoot":"","sources":["../../src/core/DependencyAwareClass.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,oBAAoB,CAAC,OAAO,SAAS,mBAAmB,GAAG,GAAG;IACrE,QAAQ,CAAC,EAAE,EAAE,mBAAmB,CAAC,OAAO,CAAC;gBAAhC,EAAE,EAAE,mBAAmB,CAAC,OAAO,CAAC;IAErD;;;;OAIG;IACH,YAAY,IAAI,mBAAmB;CAGpC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Base class for dependencies.
5
+ */
6
+ class DependencyAwareClass {
7
+ constructor(di) {
8
+ this.di = di;
9
+ }
10
+ /**
11
+ * Get dependency injection container.
12
+ *
13
+ * @deprecated Use `this.di` instead.
14
+ */
15
+ getContainer() {
16
+ return this.di;
17
+ }
18
+ }
19
+ exports.default = DependencyAwareClass;
20
+ //# sourceMappingURL=DependencyAwareClass.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DependencyAwareClass.js","sourceRoot":"","sources":["../../src/core/DependencyAwareClass.ts"],"names":[],"mappings":";;AAGA;;GAEG;AACH,MAAqB,oBAAoB;IACvC,YAAqB,EAAgC;QAAhC,OAAE,GAAF,EAAE,CAA8B;IAAG,CAAC;IAEzD;;;;OAIG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;CACF;AAXD,uCAWC"}
@@ -0,0 +1,62 @@
1
+ import { Context } from 'aws-lambda';
2
+ import DependencyAwareClass from './DependencyAwareClass';
3
+ import { LambdaWrapperConfig } from './config';
4
+ declare type Class<TInstance, TConfig extends LambdaWrapperConfig> = new (di: DependencyInjection<TConfig>) => TInstance;
5
+ /**
6
+ * Dependency injection container.
7
+ *
8
+ * Dependencies (singleton instances of dependency-aware classes) are provided
9
+ * to the main Lambda handler and other dependencies via this class.
10
+ */
11
+ export default class DependencyInjection<TConfig extends LambdaWrapperConfig = any> {
12
+ readonly config: TConfig;
13
+ readonly event: any;
14
+ readonly context: Context;
15
+ /**
16
+ * Instantiated dependencies.
17
+ */
18
+ readonly dependencies: Record<string, DependencyAwareClass>;
19
+ /**
20
+ * True until all dependencies have been constructed.
21
+ */
22
+ private isConstructing;
23
+ constructor(config: TConfig, event: any, context: Context);
24
+ /**
25
+ * Get the singleton instance of the given dependency.
26
+ *
27
+ * @param dependency
28
+ */
29
+ get<T extends DependencyAwareClass>(dependency: Class<T, TConfig>): T;
30
+ /**
31
+ * Get the event passed to AWS Lambda.
32
+ *
33
+ * @deprecated Use `di.event` instead.
34
+ */
35
+ getEvent(): any;
36
+ /**
37
+ * Get the AWS Lambda context object.
38
+ *
39
+ * @deprecated Use `di.context` instead.
40
+ */
41
+ getContext(): Context;
42
+ /**
43
+ * Get Lambda Wrapper configuration.
44
+ *
45
+ * @deprecated Use `di.config` instead.
46
+ */
47
+ getConfiguration(): TConfig;
48
+ /**
49
+ * True if the function is being executed in `serverless-offline`.
50
+ *
51
+ * We use the following checks for this:
52
+ *
53
+ * - if there is no function ARN, or the ARN includes 'offline'
54
+ * - if `process.env.USE_SERVERLESS_OFFLINE` is set
55
+ *
56
+ * TODO: This is nothing to do with dependency injection and should be moved
57
+ * somewhere else! Any ideas?
58
+ */
59
+ get isOffline(): boolean;
60
+ }
61
+ export {};
62
+ //# sourceMappingURL=DependencyInjection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DependencyInjection.d.ts","sourceRoot":"","sources":["../../src/core/DependencyInjection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,OAAO,oBAAoB,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAG/C,aAAK,KAAK,CAAC,SAAS,EAAE,OAAO,SAAS,mBAAmB,IAAI,KAAK,EAAE,EAAE,mBAAmB,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC;AAEjH;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB,CAAC,OAAO,SAAS,mBAAmB,GAAG,GAAG;IAY9E,QAAQ,CAAC,MAAM,EAAE,OAAO;IACxB,QAAQ,CAAC,KAAK,EAAE,GAAG;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO;IAb3B;;OAEG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAE5D;;OAEG;IACH,OAAO,CAAC,cAAc,CAAQ;gBAGnB,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,GAAG,EACV,OAAO,EAAE,OAAO;IAyC3B;;;;OAIG;IACH,GAAG,CAAC,CAAC,SAAS,oBAAoB,EAAE,UAAU,EAAE,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC;IAsBrE;;;;OAIG;IACH,QAAQ;IAIR;;;;OAIG;IACH,UAAU;IAIV;;;;OAIG;IACH,gBAAgB;IAIhB;;;;;;;;;;OAUG;IACH,IAAI,SAAS,IAAI,OAAO,CAIvB;CACF"}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Dependency injection container.
5
+ *
6
+ * Dependencies (singleton instances of dependency-aware classes) are provided
7
+ * to the main Lambda handler and other dependencies via this class.
8
+ */
9
+ class DependencyInjection {
10
+ constructor(config, event, context) {
11
+ this.config = config;
12
+ this.event = event;
13
+ this.context = context;
14
+ /**
15
+ * True until all dependencies have been constructed.
16
+ */
17
+ this.isConstructing = true;
18
+ // get unique dependency classes -- a class may be included several times,
19
+ // but should be instantiated only once
20
+ const classes = Array.from(new Set(Object.values(config.dependencies)));
21
+ // guard against duplicate keys
22
+ const countByName = classes
23
+ .map((Constructor) => Constructor.name)
24
+ .reduce((counts, name) => ({ ...counts, [name]: (counts[name] || 0) + 1 }), {});
25
+ if (Object.values(countByName).some((count) => count > 1)) {
26
+ const duplicateNames = Object.entries(countByName)
27
+ .filter(([, count]) => count > 1)
28
+ .map(([name]) => name);
29
+ // if all class names are single-letter, they're probably minified -- in
30
+ // this case, give a hint about how to fix it
31
+ const action = duplicateNames.every((it) => it.length === 1)
32
+ ? "If you don't recognise the single-letter names listed above, your "
33
+ + "bundler may be minifying your code. You'll need to disable this "
34
+ + 'for Lambda Wrapper to work correctly. Please refer to the Notes '
35
+ + 'section of the Lambda Wrapper readme:\n\n'
36
+ + ' https://github.com/comicrelief/lambda-wrapper/tree/beta#notes'
37
+ : 'Please ensure that all dependency classes have a unique name.';
38
+ throw new Error(`Dependency names are not unique: ${duplicateNames.join(', ')}\n\n${action}`);
39
+ }
40
+ // instantiate all dependencies
41
+ this.dependencies = Object.fromEntries(classes.map((Constructor) => [Constructor.name, new Constructor(this)]));
42
+ this.isConstructing = false;
43
+ }
44
+ /**
45
+ * Get the singleton instance of the given dependency.
46
+ *
47
+ * @param dependency
48
+ */
49
+ get(dependency) {
50
+ if (this.isConstructing) {
51
+ throw new Error('Dependencies are not available in dependency class constructors.\n\n'
52
+ + 'To fix this, call `di.get` in the function where the dependency is '
53
+ + 'used instead of inside your constructor.');
54
+ }
55
+ const name = dependency.name;
56
+ if (!this.dependencies[name]) {
57
+ throw new Error(`${name} does not exist in dependency container\n\n`
58
+ + `Make sure you've included ${name} in the 'dependencies' key of your `
59
+ + 'Lambda Wrapper config.');
60
+ }
61
+ return this.dependencies[name];
62
+ }
63
+ /**
64
+ * Get the event passed to AWS Lambda.
65
+ *
66
+ * @deprecated Use `di.event` instead.
67
+ */
68
+ getEvent() {
69
+ return this.event;
70
+ }
71
+ /**
72
+ * Get the AWS Lambda context object.
73
+ *
74
+ * @deprecated Use `di.context` instead.
75
+ */
76
+ getContext() {
77
+ return this.context;
78
+ }
79
+ /**
80
+ * Get Lambda Wrapper configuration.
81
+ *
82
+ * @deprecated Use `di.config` instead.
83
+ */
84
+ getConfiguration() {
85
+ return this.config;
86
+ }
87
+ /**
88
+ * True if the function is being executed in `serverless-offline`.
89
+ *
90
+ * We use the following checks for this:
91
+ *
92
+ * - if there is no function ARN, or the ARN includes 'offline'
93
+ * - if `process.env.USE_SERVERLESS_OFFLINE` is set
94
+ *
95
+ * TODO: This is nothing to do with dependency injection and should be moved
96
+ * somewhere else! Any ideas?
97
+ */
98
+ get isOffline() {
99
+ return !this.context.invokedFunctionArn
100
+ || this.context.invokedFunctionArn.includes('offline')
101
+ || !!process.env.USE_SERVERLESS_OFFLINE;
102
+ }
103
+ }
104
+ exports.default = DependencyInjection;
105
+ //# sourceMappingURL=DependencyInjection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DependencyInjection.js","sourceRoot":"","sources":["../../src/core/DependencyInjection.ts"],"names":[],"mappings":";;AAQA;;;;;GAKG;AACH,MAAqB,mBAAmB;IAWtC,YACW,MAAe,EACf,KAAU,EACV,OAAgB;QAFhB,WAAM,GAAN,MAAM,CAAS;QACf,UAAK,GAAL,KAAK,CAAK;QACV,YAAO,GAAP,OAAO,CAAS;QAR3B;;WAEG;QACK,mBAAc,GAAG,IAAI,CAAC;QAO5B,0EAA0E;QAC1E,uCAAuC;QACvC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAExE,+BAA+B;QAC/B,MAAM,WAAW,GAAG,OAAO;aACxB,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC;aACtC,MAAM,CACL,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAClE,EAA4B,CAC7B,CAAC;QACJ,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE;YACzD,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;iBAC/C,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;iBAChC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;YAEzB,wEAAwE;YACxE,6CAA6C;YAC7C,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC;gBAC1D,CAAC,CAAC,oEAAoE;sBAClE,kEAAkE;sBAClE,kEAAkE;sBAClE,2CAA2C;sBAC3C,iEAAiE;gBACrE,CAAC,CAAC,+DAA+D,CAAC;YAEpE,MAAM,IAAI,KAAK,CACb,oCAAoC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,MAAM,EAAE,CAC7E,CAAC;SACH;QAED,+BAA+B;QAC/B,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CACpC,OAAO,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CACxE,CAAC;QAEF,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAiC,UAA6B;QAC/D,IAAI,IAAI,CAAC,cAAc,EAAE;YACvB,MAAM,IAAI,KAAK,CACb,sEAAsE;kBACpE,qEAAqE;kBACrE,0CAA0C,CAC7C,CAAC;SACH;QAED,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;QAE7B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE;YAC5B,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,6CAA6C;kBAClD,6BAA6B,IAAI,qCAAqC;kBACtE,wBAAwB,CAC3B,CAAC;SACH;QAED,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAM,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;OAIG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,gBAAgB;QACd,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;;;;;;;OAUG;IACH,IAAI,SAAS;QACX,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,kBAAkB;eAClC,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,SAAS,CAAC;eACnD,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;IAC5C,CAAC;CACF;AA7HD,sCA6HC"}
@@ -0,0 +1,73 @@
1
+ import { Context } from '../index';
2
+ import DependencyInjection from './DependencyInjection';
3
+ import { LambdaWrapperConfig } from './config';
4
+ export interface WrapOptions {
5
+ /**
6
+ * Whether uncaught errors should be handled to return an HTTP 500 response
7
+ * instead of causing a function error. (default: `true`)
8
+ *
9
+ * This is what you usually want when working on an HTTP endpoint, but in
10
+ * other contexts (e.g. queue consumers) you may want AWS Lambda to report a
11
+ * failure so that the event is retried.
12
+ */
13
+ handleUncaughtErrors?: boolean;
14
+ }
15
+ export default class LambdaWrapper<TConfig extends LambdaWrapperConfig = LambdaWrapperConfig> {
16
+ readonly config: TConfig;
17
+ constructor(config: TConfig);
18
+ /**
19
+ * Returns a new Lambda Wrapper with the given configuration applied.
20
+ *
21
+ * @param config
22
+ */
23
+ configure<TMoreConfig>(config: Partial<TConfig> & TMoreConfig): LambdaWrapper<TConfig & TMoreConfig>;
24
+ /**
25
+ * Wrap the given function.
26
+ */
27
+ wrap<T>(handler: (di: DependencyInjection<TConfig>) => T | Promise<T>, options?: WrapOptions): (event: any, context: Context) => Promise<any>;
28
+ /**
29
+ * `true` if we will send traces to Lumigo.
30
+ *
31
+ * The `LUMIGO_TRACER_TOKEN` env var is present in both manually traced and
32
+ * auto-traced functions.
33
+ */
34
+ static get isLumigoEnabled(): boolean;
35
+ /**
36
+ * `true` if the Lambda function is already being traced by a higher-level
37
+ * Lumigo wrapper, in which case we don't need to manually wrap our handlers.
38
+ *
39
+ * There are two ways that this can be done, based on the documentation
40
+ * [here](https://docs.lumigo.io/docs/lambda-layers): using a Lambda runtime
41
+ * wrapper, or handler redirection. Each method can be detected via its
42
+ * environment variables. Auto-trace uses the runtime wrapper.
43
+ */
44
+ static get isLumigoWrappingUs(): boolean;
45
+ /**
46
+ * Process the result once we have one.
47
+ *
48
+ * @param di
49
+ * @param result
50
+ */
51
+ static handleSuccess(di: DependencyInjection, result: any): any;
52
+ /**
53
+ * Gracefully handles an error, logging in Lumigo and generating a response
54
+ * reflecting the `code` of the error, if defined.
55
+ *
56
+ * Lumigo generates alerts for logs on level ERROR. This means that
57
+ * `logger.error` will produce an alert. To avoid meaningless notifications,
58
+ * most likely coming from tests, we log INFO unless either:
59
+ *
60
+ * 1. `error.raiseOnEpsagon` is defined & truthy
61
+ * 2. `error.code` is defined and `error.code >= 500`.
62
+ *
63
+ * @param di
64
+ * @param error
65
+ * @param [throwError=false]
66
+ */
67
+ static handleError(di: DependencyInjection, error: Error, throwError?: boolean): {
68
+ statusCode: any;
69
+ headers: Record<string, string>;
70
+ body: string;
71
+ } | Error;
72
+ }
73
+ //# sourceMappingURL=LambdaWrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LambdaWrapper.d.ts","sourceRoot":"","sources":["../../src/core/LambdaWrapper.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAInC,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAe,MAAM,UAAU,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,CAAC,OAAO,OAAO,aAAa,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB;IAC9E,QAAQ,CAAC,MAAM,EAAE,OAAO;gBAAf,MAAM,EAAE,OAAO;IAEpC;;;;OAIG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,WAAW,GAAG,aAAa,CAAC,OAAO,GAAG,WAAW,CAAC;IAIpG;;OAEG;IACH,IAAI,CAAC,CAAC,EACJ,OAAO,EAAE,CAAC,EAAE,EAAE,mBAAmB,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAC7D,OAAO,CAAC,EAAE,WAAW,WAMO,GAAG,WAAW,OAAO;IAyDnD;;;;;OAKG;IACH,MAAM,KAAK,eAAe,IAAI,OAAO,CAEpC;IAED;;;;;;;;OAQG;IACH,MAAM,KAAK,kBAAkB,IAAI,OAAO,CAKvC;IAED;;;;;OAKG;IACH,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,mBAAmB,EAAE,MAAM,EAAE,GAAG;IASzD;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,UAAQ;;;;;CA8B7E"}