@tsed/platform-serverless 7.0.0-alpha.1 → 7.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 (58) hide show
  1. package/jest.config.js +4 -4
  2. package/lib/cjs/builder/PlatformServerless.js +160 -0
  3. package/lib/cjs/builder/PlatformServerless.js.map +1 -0
  4. package/lib/cjs/builder/PlatformServerlessHandler.js +93 -0
  5. package/lib/cjs/builder/PlatformServerlessHandler.js.map +1 -0
  6. package/lib/cjs/domain/ServerlessContext.js +26 -0
  7. package/lib/cjs/domain/ServerlessContext.js.map +1 -0
  8. package/lib/cjs/domain/ServerlessRequest.js +80 -0
  9. package/lib/cjs/domain/ServerlessRequest.js.map +1 -0
  10. package/lib/cjs/domain/ServerlessResponse.js +193 -0
  11. package/lib/cjs/domain/ServerlessResponse.js.map +1 -0
  12. package/lib/cjs/exports.js +16 -0
  13. package/lib/cjs/exports.js.map +1 -0
  14. package/lib/cjs/index.js +15 -0
  15. package/lib/cjs/index.js.map +1 -0
  16. package/lib/cjs/package.json +3 -0
  17. package/lib/cjs/utils/getRequestId.js +13 -0
  18. package/lib/cjs/utils/getRequestId.js.map +1 -0
  19. package/lib/cjs/utils/setResponseHeaders.js +35 -0
  20. package/lib/cjs/utils/setResponseHeaders.js.map +1 -0
  21. package/lib/esm/builder/PlatformServerless.js +133 -0
  22. package/lib/esm/builder/PlatformServerless.js.map +1 -0
  23. package/lib/esm/builder/PlatformServerlessHandler.js +90 -0
  24. package/lib/esm/builder/PlatformServerlessHandler.js.map +1 -0
  25. package/lib/esm/domain/ServerlessContext.js +22 -0
  26. package/lib/esm/domain/ServerlessContext.js.map +1 -0
  27. package/lib/esm/domain/ServerlessRequest.js +76 -0
  28. package/lib/esm/domain/ServerlessRequest.js.map +1 -0
  29. package/lib/esm/domain/ServerlessResponse.js +189 -0
  30. package/lib/esm/domain/ServerlessResponse.js.map +1 -0
  31. package/lib/esm/exports.js +4 -0
  32. package/lib/esm/exports.js.map +1 -0
  33. package/lib/esm/index.js +12 -0
  34. package/lib/esm/index.js.map +1 -0
  35. package/lib/esm/package.json +3 -0
  36. package/lib/esm/utils/getRequestId.js +9 -0
  37. package/lib/esm/utils/getRequestId.js.map +1 -0
  38. package/lib/esm/utils/setResponseHeaders.js +31 -0
  39. package/lib/esm/utils/setResponseHeaders.js.map +1 -0
  40. package/lib/{builder → types/builder}/PlatformServerless.d.ts +14 -6
  41. package/lib/{builder → types/builder}/PlatformServerlessHandler.d.ts +2 -2
  42. package/lib/{domain → types/domain}/ServerlessContext.d.ts +0 -0
  43. package/lib/{domain → types/domain}/ServerlessRequest.d.ts +0 -0
  44. package/lib/{domain → types/domain}/ServerlessResponse.d.ts +1 -1
  45. package/lib/types/exports.d.ts +3 -0
  46. package/lib/types/index.d.ts +11 -0
  47. package/lib/types/utils/getRequestId.d.ts +2 -0
  48. package/lib/{utils → types/utils}/setResponseHeaders.d.ts +0 -0
  49. package/package.json +33 -17
  50. package/readme.md +285 -68
  51. package/tsconfig.compile.esm.json +12 -0
  52. package/lib/decorators/lambda.d.ts +0 -58
  53. package/lib/index.d.ts +0 -9
  54. package/lib/index.js +0 -1020
  55. package/lib/index.js.map +0 -1
  56. package/lib/index.modern.js +0 -978
  57. package/lib/index.modern.js.map +0 -1
  58. package/lib/services/PlatformServerlessTest.d.ts +0 -40
package/readme.md CHANGED
@@ -36,16 +36,96 @@ This package allows the creation of pure Lambda AWS without mounting an Express.
36
36
  It supports:
37
37
 
38
38
  - Creating multiple lambda in one file,
39
+ - Support routing,
39
40
  - DI injection with `@tsed/di`,
40
41
  - Models mapping using `@tsed/schema` and `@tsed/json-mapper`,
41
42
  - Params decorators can be used from `@tsed/platform-params` to get Query, Body, etc...
43
+ - Response can be modified by using `@tsed/platform-response-filter`,
44
+ - All error can be handled by using `@tsed/platform-exceptions`,
42
45
  - Operation descriptions like `@Returns`,
43
46
  - `@tsed/async-hook-context` to inject Context anywhere in your class!
47
+ - All ORM already available for other Ts.ED platform.
48
+
49
+ ## Unsupported features
50
+
51
+ Some features that you can use with the Express.js or Koa.js platforms are not available with the Serverless
52
+ platform.
53
+
54
+ (See also our [table feature capabilities](https://tsed.io/getting-started/#platform-features-support)).
55
+
56
+ ### Middlewares
57
+
58
+ This mechanism is specific to Koa.js / Express.js and doesn't exist with the Serverless approach.
59
+
60
+ Serverless provides a number of features such as Cors management via the configuration of `serverless.yml` and doesn't
61
+ need to use middleware. In addition, serverless [plugins](https://www.serverless.com/plugins/) are also available and
62
+ allow you to manage advanced scenarios without having to develop anything!
63
+
64
+ If you can't find what you are looking for on the Serverless side, you can use `@tsed/di`'
65
+ s [Interceptors](/docs/interceptors.md) to decorate the methods and add shareable features.
66
+
67
+ ### Passport/OIDC/GraphQL etc...
68
+
69
+ Because middlewares aren't supported Passport.js, OIDC, GraphQL won't be usable in this platform either.
70
+
71
+ ### Upload files
72
+
73
+ File upload isn't covered at this time. Any help is welcome to improve this platform :).
74
+
75
+ ### Statics files
76
+
77
+ The goal of lambda isn't to expose static files. We do not plan to support this use in the near future.
78
+
79
+ ## Rule
80
+
81
+ ::: warning By convention, try to not import something from `@tsed/common`. `@tsed/common` embed a lot of codes designed
82
+ for the Full server platform which are not necessary in the Serverless context and aren't optimized for it.
83
+
84
+ The recent version of Ts.ED expose all necessary decorators from `@tsed/schema`, `@tsed/platform-params` or `@tsed/di`.
85
+ For example, @@Get@@ or @@Post@@ are commonly imported like this:
86
+
87
+ ```typescript
88
+ import {Get} from "@tsed/common";
89
+ ```
90
+
91
+ Now, you have to import the decorator from `@tsed/schema`.
92
+
93
+ ```typescript
94
+ import {Get} from "@tsed/schema";
95
+ ```
96
+
97
+ :::
44
98
 
45
99
  ## Installation
46
100
 
101
+ Generate a new project with the CLI (you can also start from an existing project):
102
+
47
103
  ```bash
48
- npm install --save @tsed/platform-serverless
104
+ tsed init .
105
+ ? Choose the target platform: Express.js
106
+ ? Choose the architecture for your project: Ts.ED
107
+ ? Choose the convention file styling: Ts.ED
108
+ ? Check the features needed for your project Swagger, Testing, Linter
109
+ ? Choose unit framework Jest
110
+ ? Choose linter tools framework EsLint
111
+ ? Choose extra linter tools Prettier, Lint on commit
112
+ ? Choose the package manager: Yarn
113
+ ```
114
+
115
+ This tutorial works also with NPM package manager!
116
+
117
+ Yarn:
118
+
119
+ ```bash
120
+ yarn add @tsed/platform-serverless serverless serverless-offline
121
+ yarn add -D @types/aws-lambda
122
+ ```
123
+
124
+ Npm:
125
+
126
+ ```bash
127
+ npm install --save @tsed/platform-serverless serverless serverless-offline
128
+ npm install --save-dev @types/aws-lambda
49
129
  ```
50
130
 
51
131
  ## Configuration
@@ -53,110 +133,162 @@ npm install --save @tsed/platform-serverless
53
133
  In the `src/lambda` create a new Lambda class:
54
134
 
55
135
  ```typescript
56
- import {Injectable} from "@tsed/di";
57
- import {Returns, Summary} from "@tsed/schema";
58
- import {QueryParams} from "@tsed/platform-serverless"; // /!\ don't import decorators from @tsed/common
136
+ import {Controller, Inject} from "@tsed/di";
137
+ import {Get, Returns, Summary} from "@tsed/schema";
138
+ import {QueryParams} from "@tsed/platform-params"; // /!\ don't import decorators from @tsed/common
59
139
  import {TimeslotsService} from "../services/TimeslotsService";
60
140
  import {TimeslotModel} from "../models/TimeslotModel";
61
141
 
62
- @Injectable()
142
+ @Controller("/timeslots")
63
143
  export class TimeslotsLambda {
64
144
  @Inject()
65
145
  protected timeslotsService: TimeslotsService;
66
146
 
67
- @Lambda()
147
+ @Get("/")
68
148
  @Summary("Return a list of timeslots")
69
149
  @Returns(200, Array).Of(TimeslotModel)
70
150
  get(@QueryParams("date_start") dateStart: Date, @QueryParams("date_end") dateEnd: Date) {
71
151
  return this.timeslotsService.find({
72
152
  dateStart,
73
153
  dateEnd
74
- })
154
+ });
75
155
  }
76
156
  }
77
157
  ```
78
158
 
79
- Create new handler.ts to expose your lambda:
159
+ Create new `handler.ts` to expose your lambda:
80
160
 
81
161
  ```typescript
82
- import {PlatformServerless} from "@tsed/serverless";
162
+ import {PlatformServerless} from "@tsed/platform-serverless";
83
163
  import {TimeslotsLambda} from "./lambda/TimeslotsLambda";
84
164
 
85
165
  const platform = PlatformServerless.bootstrap({
86
- lambda: [
87
- TimeslotsLambda
88
- ]
166
+ lambda: [TimeslotsLambda]
89
167
  // put your Application configuration here
90
- })
168
+ });
91
169
 
92
170
  // then export the lambda
93
171
  export = platform.callbacks();
94
172
  ```
95
173
 
96
- Finally, create the serverless.yml:
174
+ Finally, create the `serverless.yml`:
97
175
 
98
176
  ```yml
99
- # Welcome to Serverless!
100
- #
101
- # This file is the main config file for your service.
102
- # It's very minimal at this point and uses default values.
103
- # You can always add more config options for more control.
104
- # We've included some commented out config examples here.
105
- # Just uncomment any of them to get that config option.
106
- #
107
- # For full config options, check the docs:
108
- # docs.serverless.com
109
- #
110
- # Happy Coding!
111
-
112
177
  service: timeslots
113
- # app and org for use with dashboard.serverless.com
114
- #app: your-app-name
115
- #org: your-org-name
116
178
 
117
- # You can pin your service to only deploy with a specific Serverless version
118
- # Check out our docs for more details
119
- frameworkVersion: '2'
179
+ frameworkVersion: "2"
120
180
 
121
181
  provider:
122
182
  name: aws
123
- runtime: nodejs12.x
124
- lambdaHashingVersion: 20201221
183
+ runtime: nodejs14.x
184
+ lambdaHashingVersion: "20201221"
185
+
186
+ plugins:
187
+ - serverless-offline
125
188
 
126
189
  functions:
127
190
  timeslots:
128
- handler: handler.getTimeslots
191
+ handler: dist/handler.getTimeslots
129
192
  events:
130
193
  - http:
131
- path: /timeslots
132
- method: get
133
- cors: true
194
+ path: /timeslots
195
+ method: get
196
+ ```
197
+
198
+ ## Invoke a lambda with serverless
199
+
200
+ Serverless provide a plugin named `serverless-offline`. This Serverless plugin emulates AWS λ and API Gateway on your
201
+ local machine to speed up your development cycles. To do so, it starts an HTTP server that handles the request's
202
+ lifecycle like API does and invokes your handlers.
203
+
204
+ So, by using the `serverless offline` command, we'll be able to invoke our function. For that, we need also to build our
205
+ code before invoke the lambda.
206
+
207
+ To simplify our workflow, we can add the following npm script command in our `package.json`:
208
+
209
+ ```json
210
+ {
211
+ "scripts": {
212
+ "invoke:timeslots": "yarn build && serverless invoke local -f timeslots"
213
+ }
214
+ }
215
+ ```
216
+
217
+ Now, we can run the following command to invoke our lambda:
218
+
219
+ ```
220
+ yarn invoke:timeslots
221
+ // OR
222
+ npm run invoke:timeslots
223
+ ```
224
+
225
+ You should see in the terminal the following result:
226
+
227
+ ```json
228
+ {
229
+ "statusCode": 200,
230
+ "body": "[{\"id\":\"b6de4fc7-faaa-4cd7-a144-42f6af0dec6b\",\"title\":\"title\",\"description\":\"description\",\"start_date\":\"2021-10-29T10:40:57.019Z\",\"end_date\":\"2021-10-29T10:40:57.019Z\",\"created_at\":\"2021-10-29T10:40:57.019Z\",\"update_at\":\"2021-10-29T10:40:57.019Z\"}]",
231
+ "headers": {
232
+ "content-type": "application/json",
233
+ "x-request-id": "ebb52d5e-113b-40da-b34e-c14811df596b"
234
+ },
235
+ "isBase64Encoded": false
236
+ }
134
237
  ```
135
238
 
136
- ## Multiple lambda
239
+ ## Manage routes from code
240
+
241
+ Declaring all routes in the `serverless.yml` file can be a source of error. `@tsed/platform-serverless` can handle all
242
+ routes and call the right lambda based on the decorators like `@Get`, `@Post`, etc...
243
+
244
+ To use the embed router, change the `serverless.yml` declaration by this example:
245
+
246
+ ```yml
247
+ service: timeslots
248
+
249
+ frameworkVersion: "2"
137
250
 
138
- In you lambda controller use Lambda decorators:
251
+ provider:
252
+ name: aws
253
+ runtime: nodejs14.x
254
+ lambdaHashingVersion: "20201221"
255
+
256
+ plugins:
257
+ - serverless-offline
258
+
259
+ functions:
260
+ any:
261
+ handler: dist/handler.handler
262
+ events:
263
+ - http:
264
+ method: ANY
265
+ path: /
266
+ - http:
267
+ method: ANY
268
+ path: "{proxy+}"
269
+ ```
270
+
271
+ Then, edit the `handler.ts` and change the exported functions:
139
272
 
140
273
  ```typescript
141
- import {Injectable} from "@tsed/di";
142
- import {Returns, Summary} from "@tsed/schema";
143
- import {QueryParams} from "@tsed/platform-serverless"; // /!\ don't import decorators from @tsed/common
144
- import {TimeslotsService} from "../services/TimeslotsService";
145
- import {TimeslotModel} from "../models/TimeslotModel";
274
+ import {PlatformServerless} from "@tsed/platform-serverless";
275
+ import {TimeslotsLambda} from "./TimeslotsLambda";
146
276
 
147
- @Injectable()
148
- export class TimeslotsLambda {
149
- @Inject()
150
- protected timeslotsService: TimeslotsService;
277
+ const platform = PlatformServerless.bootstrap({
278
+ lambda: [TimeslotsLambda]
279
+ });
151
280
 
152
- @Lambda()
153
- @Summary("Return a list of timeslots")
154
- @Returns(200, Array).Of(TimeslotModel)
155
- get(@QueryParams("date_start") dateStart: Date, @QueryParams("date_end") dateEnd: Date) {
156
- return this.timeslotsService.find({
157
- dateStart,
158
- dateEnd
159
- })
281
+ export const handler = platform.handler();
282
+ ```
283
+
284
+ Now, Ts.ED will handle request and call the expected lambda in your controllers.
285
+
286
+ To simplify our workflow, we can add the following npm script command in our `package.json`:
287
+
288
+ ```json
289
+ {
290
+ "scripts": {
291
+ "invoke:any": "yarn serverless invoke local -f any --data '{\"path\":\"/timeslots\", \"httpMethod\": \"GET\"}'"
160
292
  }
161
293
  }
162
294
  ```
@@ -172,31 +304,116 @@ import {ServerlessContext} from "./ServerlessContext";
172
304
  @Injectable()
173
305
  export class TimeslotsLambda {
174
306
  get(@Context() $ctx: ServerlessContext) {
175
- console.log($ctx.context) // AWS Context
176
- console.log($ctx.event) // AWS Event
177
- console.log($ctx.response) // Response Platform abstraction layer
178
- console.log($ctx.request) // Request Platform abstraction layer
179
-
180
- $ctx.response.setHeader('x-test', 'test');
181
-
182
- return {}
307
+ console.log($ctx.context); // AWS Context
308
+ console.log($ctx.event); // AWS Event
309
+ console.log($ctx.response); // Response Platform abstraction layer
310
+ console.log($ctx.request); // Request Platform abstraction layer
311
+
312
+ $ctx.response.setHeader("x-test", "test");
313
+
314
+ return {};
315
+ }
316
+ }
317
+ ```
318
+
319
+ ## Testing
320
+
321
+ Ts.ED provide a way to test you lambda with mocked Aws event and context by using the `PlatformServerlessTest` util.
322
+
323
+ Here an example to test a Lambda controller:
324
+
325
+ ```typescript
326
+ import {PlatformServerlessTest} from "@tsed/platform-serverless-testing";
327
+ import {PlatformServerless} from "@tsed/platform-serverless";
328
+
329
+ @Controller("/")
330
+ class TimeslotsLambdaController {
331
+ @Get("/")
332
+ getAll() {
333
+ return [];
334
+ }
335
+
336
+ @Get("/:id")
337
+ getById(@PathParams("id") id: string, @QueryParams("start_date") startDate: Date, @QueryParams("end_date") endDate: Date) {
338
+ return {
339
+ id,
340
+ startDate,
341
+ endDate
342
+ };
183
343
  }
184
344
  }
345
+
346
+ describe("TimeslotsLambdaController", () => {
347
+ beforeEach(
348
+ PlatformServerlessTest.bootstrap(PlatformServerless, {
349
+ lambda: [TimeslotsLambdaController]
350
+ })
351
+ );
352
+ afterEach(() => PlatformServerlessTest.reset());
353
+
354
+ describe("Invoke by lambda name", () => {
355
+ it("should call getAll Lambda", async () => {
356
+ const response = await PlatformServerlessTest.request.call("getAll");
357
+
358
+ expect(response.statusCode).toEqual(200);
359
+ expect(response.headers).toEqual({
360
+ "x-request-id": "requestId",
361
+ "content-type": "application/json"
362
+ });
363
+ expect(JSON.parse(response.body)).toEqual([]);
364
+ });
365
+
366
+ it("should call getAll Lambda", async () => {
367
+ const response = await PlatformServerlessTest.request
368
+ .call("getById")
369
+ .params({
370
+ id: "1"
371
+ })
372
+ .query({
373
+ start_date: new Date("2020-01-01"),
374
+ end_date: new Date("2020-01-10")
375
+ });
376
+
377
+ expect(response.statusCode).toEqual(200);
378
+ expect(response.headers).toEqual({
379
+ "x-request-id": "requestId",
380
+ "content-type": "application/json"
381
+ });
382
+ expect(JSON.parse(response.body)).toEqual({
383
+ id: "1",
384
+ endDate: "2020-01-10T00:00:00.000Z",
385
+ startDate: "2020-01-01T00:00:00.000Z"
386
+ });
387
+ });
388
+ });
389
+
390
+ describe("invoke using the router", () => {
391
+ it("should call getAll Lambda", async () => {
392
+ const response = await PlatformServerlessTest.request.get("/");
393
+
394
+ expect(response.statusCode).toEqual(200);
395
+ expect(response.headers).toEqual({
396
+ "x-request-id": "requestId",
397
+ "content-type": "application/json"
398
+ });
399
+ expect(JSON.parse(response.body)).toEqual([]);
400
+ });
401
+ });
402
+ });
185
403
  ```
186
404
 
187
405
  ## Contributors
406
+
188
407
  Please read [contributing guidelines here](https://tsed.io/CONTRIBUTING.html).
189
408
 
190
409
  <a href="https://github.com/tsedio/ts-express-decorators/graphs/contributors"><img src="https://opencollective.com/tsed/contributors.svg?width=890" /></a>
191
410
 
192
-
193
411
  ## Backers
194
412
 
195
413
  Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/tsed#backer)]
196
414
 
197
415
  <a href="https://opencollective.com/tsed#backers" target="_blank"><img src="https://opencollective.com/tsed/tiers/backer.svg?width=890"></a>
198
416
 
199
-
200
417
  ## Sponsors
201
418
 
202
419
  Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/tsed#sponsor)]
@@ -0,0 +1,12 @@
1
+ {
2
+ "extends": "./tsconfig.compile.json",
3
+ "compilerOptions": {
4
+ "baseUrl": ".",
5
+ "module": "ES2020",
6
+ "rootDir": "src",
7
+ "outDir": "./lib/esm",
8
+ "declaration": true,
9
+ "declarationDir": "./lib/types"
10
+ },
11
+ "exclude": ["node_modules", "test", "lib", "benchmark", "coverage", "spec", "**/*.benchmark.ts", "**/*.spec.ts", "keys", "jest.config.js"]
12
+ }
@@ -1,58 +0,0 @@
1
- import { OperationMethods } from "@tsed/schema";
2
- export interface LambdaOptions {
3
- name?: string;
4
- path: string;
5
- method: OperationMethods | string;
6
- }
7
- export interface LambdaChainedDecorators {
8
- <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>): TypedPropertyDescriptor<T> | void;
9
- /**
10
- *
11
- * @param name
12
- */
13
- Name(name: string): this;
14
- Method(method: string): this;
15
- Path(path: string): this;
16
- }
17
- /**
18
- * Declare a lambda endpoint.
19
- *
20
- * ```typescript
21
- * export class Lambda {
22
- *
23
- * }
24
- * ```
25
- *
26
- * @decorator
27
- */
28
- export declare function Lambda(name?: string): LambdaChainedDecorators;
29
- /**
30
- * @param path
31
- * @decorators
32
- * @ignore
33
- */
34
- export declare function Get(path?: string): LambdaChainedDecorators;
35
- /**
36
- * @param path
37
- * @decorators
38
- * @ignore
39
- */
40
- export declare function Post(path?: string): LambdaChainedDecorators;
41
- /**
42
- * @param path
43
- * @decorators
44
- * @ignore
45
- */
46
- export declare function Put(path?: string): LambdaChainedDecorators;
47
- /**
48
- * @param path
49
- * @decorators
50
- * @ignore
51
- */
52
- export declare function Patch(path?: string): LambdaChainedDecorators;
53
- /**
54
- * @param path
55
- * @decorators
56
- * @ignore
57
- */
58
- export declare function Delete(path?: string): LambdaChainedDecorators;
package/lib/index.d.ts DELETED
@@ -1,9 +0,0 @@
1
- export * from "@tsed/di";
2
- export * from "@tsed/platform-params";
3
- export * from "./builder/PlatformServerlessHandler";
4
- export * from "./builder/PlatformServerless";
5
- export * from "./services/PlatformServerlessTest";
6
- export * from "./decorators/lambda";
7
- export * from "./domain/ServerlessContext";
8
- export * from "./domain/ServerlessResponse";
9
- export * from "./domain/ServerlessResponse";