@tsed/platform-serverless 8.0.1 → 8.0.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.
- package/package.json +14 -13
- package/src/builder/PlatformServerless.spec.ts +219 -0
- package/src/builder/PlatformServerless.ts +202 -0
- package/src/builder/PlatformServerlessHandler.spec.ts +145 -0
- package/src/builder/PlatformServerlessHandler.ts +121 -0
- package/src/domain/ServerlessContext.spec.ts +106 -0
- package/src/domain/ServerlessContext.ts +60 -0
- package/src/domain/ServerlessEvent.ts +24 -0
- package/src/domain/ServerlessRequest.spec.ts +130 -0
- package/src/domain/ServerlessRequest.ts +106 -0
- package/src/domain/ServerlessResponse.spec.ts +139 -0
- package/src/domain/ServerlessResponse.ts +226 -0
- package/src/domain/ServerlessResponseStream.spec.ts +63 -0
- package/src/domain/ServerlessResponseStream.ts +113 -0
- package/src/exports.ts +1 -0
- package/src/index.ts +14 -0
- package/src/utils/getRequestId.spec.ts +43 -0
- package/src/utils/getRequestId.ts +16 -0
- package/src/utils/isInAWS.ts +8 -0
- package/src/utils/setResponseHeaders.spec.ts +156 -0
- package/src/utils/setResponseHeaders.ts +58 -0
package/package.json
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
"name": "@tsed/platform-serverless",
|
|
3
3
|
"description": "Platform serverless module for Ts.ED Framework",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.0.
|
|
5
|
+
"version": "8.0.2",
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
7
|
"main": "./lib/esm/index.js",
|
|
8
8
|
"module": "./lib/esm/index.js",
|
|
9
9
|
"typings": "./lib/types/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
|
+
"@tsed/source": "./src/index.ts",
|
|
12
13
|
"types": "./lib/types/index.d.ts",
|
|
13
14
|
"import": "./lib/esm/index.js",
|
|
14
15
|
"default": "./lib/esm/index.js"
|
|
@@ -22,23 +23,23 @@
|
|
|
22
23
|
"test:ci": "vitest run --coverage.thresholds.autoUpdate=true"
|
|
23
24
|
},
|
|
24
25
|
"dependencies": {
|
|
25
|
-
"@tsed/core": "8.0.
|
|
26
|
-
"@tsed/di": "8.0.
|
|
27
|
-
"@tsed/exceptions": "8.0.
|
|
28
|
-
"@tsed/hooks": "8.0.
|
|
29
|
-
"@tsed/json-mapper": "8.0.
|
|
30
|
-
"@tsed/platform-exceptions": "8.0.
|
|
31
|
-
"@tsed/platform-params": "8.0.
|
|
32
|
-
"@tsed/schema": "8.0.
|
|
26
|
+
"@tsed/core": "8.0.2",
|
|
27
|
+
"@tsed/di": "8.0.2",
|
|
28
|
+
"@tsed/exceptions": "8.0.2",
|
|
29
|
+
"@tsed/hooks": "8.0.2",
|
|
30
|
+
"@tsed/json-mapper": "8.0.2",
|
|
31
|
+
"@tsed/platform-exceptions": "8.0.2",
|
|
32
|
+
"@tsed/platform-params": "8.0.2",
|
|
33
|
+
"@tsed/schema": "8.0.2",
|
|
33
34
|
"encodeurl": "^2.0.0",
|
|
34
35
|
"mime": "^4.0.4",
|
|
35
36
|
"tslib": "2.7.0",
|
|
36
37
|
"uuid": "^10.0.0"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"@tsed/barrels": "8.0.
|
|
40
|
-
"@tsed/platform-serverless-testing": "8.0.
|
|
41
|
-
"@tsed/typescript": "8.0.
|
|
40
|
+
"@tsed/barrels": "8.0.2",
|
|
41
|
+
"@tsed/platform-serverless-testing": "8.0.2",
|
|
42
|
+
"@tsed/typescript": "8.0.2",
|
|
42
43
|
"@types/aws-lambda": "^8.10.145",
|
|
43
44
|
"@types/encodeurl": "^1.0.2",
|
|
44
45
|
"@types/mime": "^4.0.0",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"vitest": "2.1.2"
|
|
49
50
|
},
|
|
50
51
|
"peerDependencies": {
|
|
51
|
-
"@tsed/di": "8.0.
|
|
52
|
+
"@tsed/di": "8.0.2",
|
|
52
53
|
"@tsed/logger": ">=6.7.5",
|
|
53
54
|
"find-my-way": ">=7.0.0"
|
|
54
55
|
},
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {Controller, Inject, Injectable} from "@tsed/di";
|
|
2
|
+
import {BodyParams, PathParams, QueryParams} from "@tsed/platform-params";
|
|
3
|
+
import {createFakeContext, createFakeEvent, PlatformServerlessTest} from "@tsed/platform-serverless-testing";
|
|
4
|
+
import {Delete, Get, Patch, Post, Put} from "@tsed/schema";
|
|
5
|
+
|
|
6
|
+
import {PlatformServerless} from "./PlatformServerless.js";
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
class TimeslotsService {
|
|
10
|
+
get() {
|
|
11
|
+
return "test";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Controller("/")
|
|
16
|
+
class TimeslotsLambdaController {
|
|
17
|
+
@Inject()
|
|
18
|
+
protected timeslotsService: TimeslotsService;
|
|
19
|
+
|
|
20
|
+
@(Get("/:id").Name("byID"))
|
|
21
|
+
getByID(@PathParams("id") id: string) {
|
|
22
|
+
return {
|
|
23
|
+
id
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Get("/")
|
|
28
|
+
get(@QueryParams("start_date") startDate: Date, @QueryParams("end_date") endDate: Date) {
|
|
29
|
+
return {
|
|
30
|
+
value: this.timeslotsService.get(),
|
|
31
|
+
startDate,
|
|
32
|
+
endDate
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Post("/")
|
|
37
|
+
post(@BodyParams() body: any) {
|
|
38
|
+
return body;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@Put("/:id")
|
|
42
|
+
put(@BodyParams() body: any) {
|
|
43
|
+
return body;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Patch("/:id")
|
|
47
|
+
patch(@BodyParams() body: any) {
|
|
48
|
+
return body;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Delete("/:id")
|
|
52
|
+
delete(@BodyParams() body: any) {
|
|
53
|
+
return body;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
other() {}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
describe("PlatformServerless", () => {
|
|
60
|
+
describe("bootstrap()", () => {
|
|
61
|
+
beforeEach(
|
|
62
|
+
PlatformServerlessTest.bootstrap(PlatformServerless, {
|
|
63
|
+
lambda: [TimeslotsLambdaController]
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
afterEach(() => PlatformServerlessTest.reset());
|
|
67
|
+
|
|
68
|
+
it("should load lambda", () => {
|
|
69
|
+
const handlers = PlatformServerlessTest.callbacks;
|
|
70
|
+
|
|
71
|
+
expect(handlers).toHaveProperty("get");
|
|
72
|
+
expect(handlers).toHaveProperty("byID");
|
|
73
|
+
});
|
|
74
|
+
it("should call lambda", async () => {
|
|
75
|
+
const response = await PlatformServerlessTest.request.call("get").query({
|
|
76
|
+
start_date: new Date("2020-01-01"),
|
|
77
|
+
end_date: new Date("2020-01-10")
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(response.statusCode).toEqual(200);
|
|
81
|
+
expect(response.headers).toEqual({
|
|
82
|
+
"x-request-id": "requestId",
|
|
83
|
+
"content-type": "application/json"
|
|
84
|
+
});
|
|
85
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
86
|
+
endDate: "2020-01-10T00:00:00.000Z",
|
|
87
|
+
startDate: "2020-01-01T00:00:00.000Z",
|
|
88
|
+
value: "test"
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
it("should call lambda with custom x-request-id", async () => {
|
|
92
|
+
const response = await PlatformServerlessTest.request
|
|
93
|
+
.call("get")
|
|
94
|
+
.headers({
|
|
95
|
+
"x-request-id": "request-id"
|
|
96
|
+
})
|
|
97
|
+
.query({
|
|
98
|
+
start_date: new Date("2020-01-01"),
|
|
99
|
+
end_date: new Date("2020-01-10")
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(response.statusCode).toEqual(200);
|
|
103
|
+
expect(response.headers).toEqual({
|
|
104
|
+
"x-request-id": "request-id",
|
|
105
|
+
"content-type": "application/json"
|
|
106
|
+
});
|
|
107
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
108
|
+
endDate: "2020-01-10T00:00:00.000Z",
|
|
109
|
+
startDate: "2020-01-01T00:00:00.000Z",
|
|
110
|
+
value: "test"
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
it("should call lambda from handler()", async () => {
|
|
114
|
+
const response = await PlatformServerlessTest.request.get("/").query({
|
|
115
|
+
start_date: new Date("2020-01-01"),
|
|
116
|
+
end_date: new Date("2020-01-10")
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(response.statusCode).toEqual(200);
|
|
120
|
+
expect(response.headers).toEqual({
|
|
121
|
+
"x-request-id": "requestId",
|
|
122
|
+
"content-type": "application/json"
|
|
123
|
+
});
|
|
124
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
125
|
+
endDate: "2020-01-10T00:00:00.000Z",
|
|
126
|
+
startDate: "2020-01-01T00:00:00.000Z",
|
|
127
|
+
value: "test"
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
it("should call lambda from handler() with params", async () => {
|
|
131
|
+
const response = await PlatformServerlessTest.request.get("/1");
|
|
132
|
+
|
|
133
|
+
expect(response.statusCode).toEqual(200);
|
|
134
|
+
expect(response.headers).toEqual({
|
|
135
|
+
"x-request-id": "requestId",
|
|
136
|
+
"content-type": "application/json"
|
|
137
|
+
});
|
|
138
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
139
|
+
id: "1"
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
it("should call lambda from handler() (post)", async () => {
|
|
143
|
+
const response = await PlatformServerlessTest.request.post("/").body({
|
|
144
|
+
label: "label"
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(response.statusCode).toEqual(200);
|
|
148
|
+
expect(response.headers).toEqual({
|
|
149
|
+
"x-request-id": "requestId",
|
|
150
|
+
"content-type": "application/json"
|
|
151
|
+
});
|
|
152
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
153
|
+
label: "label"
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
it("should call lambda from handler() (put)", async () => {
|
|
157
|
+
const response = await PlatformServerlessTest.request.put("/1").body({
|
|
158
|
+
label: "label"
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(response.statusCode).toEqual(200);
|
|
162
|
+
expect(response.headers).toEqual({
|
|
163
|
+
"x-request-id": "requestId",
|
|
164
|
+
"content-type": "application/json"
|
|
165
|
+
});
|
|
166
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
167
|
+
label: "label"
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
it("should call lambda from handler() (patch)", async () => {
|
|
171
|
+
const response = await PlatformServerlessTest.request.patch("/1").body({
|
|
172
|
+
label: "label"
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(response.statusCode).toEqual(200);
|
|
176
|
+
expect(response.headers).toEqual({
|
|
177
|
+
"x-request-id": "requestId",
|
|
178
|
+
"content-type": "application/json"
|
|
179
|
+
});
|
|
180
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
181
|
+
label: "label"
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
it("should call lambda from handler() (delete)", async () => {
|
|
185
|
+
const response = await PlatformServerlessTest.request.delete("/1").body({
|
|
186
|
+
label: "label"
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
expect(response.statusCode).toEqual(200);
|
|
190
|
+
expect(response.headers).toEqual({
|
|
191
|
+
"x-request-id": "requestId",
|
|
192
|
+
"content-type": "application/json"
|
|
193
|
+
});
|
|
194
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
195
|
+
label: "label"
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
it("should call lambda from handler() but returns a 404 if controller doesn't match and endpoint", async () => {
|
|
199
|
+
const event = createFakeEvent();
|
|
200
|
+
event.httpMethod = "GET";
|
|
201
|
+
event.path = "/test/other";
|
|
202
|
+
const context = createFakeContext();
|
|
203
|
+
const response = await PlatformServerlessTest.instance.handler()(event, context);
|
|
204
|
+
|
|
205
|
+
expect(response.statusCode).toEqual(404);
|
|
206
|
+
expect(response.headers).toEqual({"x-request-id": "requestId"});
|
|
207
|
+
expect(response.body).toEqual("Not found");
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
describe("callback()", () => {
|
|
211
|
+
it("should create handler from given parameters", () => {
|
|
212
|
+
const handler = PlatformServerless.callback(TimeslotsLambdaController, "get", {
|
|
213
|
+
// settings
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(handler).toBeInstanceOf(Function);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import {Env, Type} from "@tsed/core";
|
|
2
|
+
import {configuration, constant, createContainer, destroyInjector, injector, InjectorService, setLoggerConfiguration} from "@tsed/di";
|
|
3
|
+
import {$asyncEmit} from "@tsed/hooks";
|
|
4
|
+
import {$log} from "@tsed/logger";
|
|
5
|
+
import {getOperationsRoutes, JsonEntityStore} from "@tsed/schema";
|
|
6
|
+
import type {Context, Handler} from "aws-lambda";
|
|
7
|
+
import type {HTTPMethod, Instance} from "find-my-way";
|
|
8
|
+
|
|
9
|
+
import {ServerlessContext} from "../domain/ServerlessContext.js";
|
|
10
|
+
import type {ServerlessEvent} from "../domain/ServerlessEvent.js";
|
|
11
|
+
import {type RequestHandler, ServerlessResponseStream} from "../domain/ServerlessResponseStream.js";
|
|
12
|
+
import {getRequestId} from "../utils/getRequestId.js";
|
|
13
|
+
import {PlatformServerlessHandler} from "./PlatformServerlessHandler.js";
|
|
14
|
+
|
|
15
|
+
export interface PlatformServerlessSettings extends Partial<TsED.Configuration> {
|
|
16
|
+
lambda?: Type[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @platform
|
|
21
|
+
*/
|
|
22
|
+
export class PlatformServerless {
|
|
23
|
+
readonly name: string = "PlatformServerless";
|
|
24
|
+
private _router: Instance<any>;
|
|
25
|
+
private _promise: Promise<any>;
|
|
26
|
+
|
|
27
|
+
get injector(): InjectorService {
|
|
28
|
+
return injector();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get settings() {
|
|
32
|
+
return configuration();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get promise() {
|
|
36
|
+
return this._promise;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static bootstrap(settings: Partial<TsED.Configuration> & {lambda?: Type[]} = {}): PlatformServerless {
|
|
40
|
+
const platform = new PlatformServerless();
|
|
41
|
+
platform.createInjector(settings);
|
|
42
|
+
|
|
43
|
+
return platform;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create a new handler from the given token and propertyKey. No routing is used here.
|
|
48
|
+
*/
|
|
49
|
+
static callback(
|
|
50
|
+
token: Type<any>,
|
|
51
|
+
propertyKey: string,
|
|
52
|
+
settings: Partial<TsED.Configuration> = {}
|
|
53
|
+
): Handler<ServerlessEvent> | RequestHandler {
|
|
54
|
+
const platform = PlatformServerless.bootstrap({
|
|
55
|
+
...settings,
|
|
56
|
+
lambda: [token]
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return platform.callback(token, propertyKey);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public handler(): Handler {
|
|
63
|
+
return async (event, context) => {
|
|
64
|
+
const [router] = await Promise.all([this.initRouter(), this.init()]);
|
|
65
|
+
const result = router.find(event.httpMethod as any, event.path);
|
|
66
|
+
|
|
67
|
+
if (result) {
|
|
68
|
+
const {handler, params} = result;
|
|
69
|
+
|
|
70
|
+
event.pathParameters = {
|
|
71
|
+
...(event.pathParameters || {}),
|
|
72
|
+
...params
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return (handler as any)(event, context);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
statusCode: 404,
|
|
80
|
+
body: "Not found",
|
|
81
|
+
headers: {
|
|
82
|
+
"x-request-id": getRequestId(event, context)
|
|
83
|
+
},
|
|
84
|
+
isBase64Encoded: false
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public callbacks(tokens: Type | Type[] = [], callbacks: any = {}): Record<string, Handler> {
|
|
90
|
+
return configuration()
|
|
91
|
+
.get<Type[]>("lambda", [])
|
|
92
|
+
.concat(tokens)
|
|
93
|
+
.reduce((callbacks, token) => {
|
|
94
|
+
const routes = getOperationsRoutes(token);
|
|
95
|
+
|
|
96
|
+
return routes.reduce((callbacks, operationRoute) => {
|
|
97
|
+
const {operationId, token, propertyName, method, url} = operationRoute;
|
|
98
|
+
|
|
99
|
+
// istanbul ignore next
|
|
100
|
+
if (method === "USE") {
|
|
101
|
+
return callbacks;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const callback = this.callback(token, propertyName);
|
|
105
|
+
|
|
106
|
+
this._router?.on(method as HTTPMethod, url as string, callback as any);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
...callbacks,
|
|
110
|
+
[operationId]: callback
|
|
111
|
+
};
|
|
112
|
+
}, callbacks);
|
|
113
|
+
}, callbacks);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public async ready() {
|
|
117
|
+
await $asyncEmit("$onReady");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async stop() {
|
|
121
|
+
await destroyInjector();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public init() {
|
|
125
|
+
if (!this._promise) {
|
|
126
|
+
this._promise = this.loadInjector().then(() => this.ready());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return this._promise;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
protected callback(token: Type<any>, propertyKey: string): Handler<ServerlessEvent> | RequestHandler {
|
|
133
|
+
const entity = JsonEntityStore.fromMethod(token, propertyKey);
|
|
134
|
+
let handler: ($ctx: ServerlessContext<ServerlessEvent>) => Promise<unknown>;
|
|
135
|
+
|
|
136
|
+
const wrappedHandler = async (event: ServerlessEvent, responseStream: ServerlessResponseStream | undefined, context: Context) => {
|
|
137
|
+
await this.init();
|
|
138
|
+
|
|
139
|
+
if (!handler) {
|
|
140
|
+
const platformHandler = this.injector.get<PlatformServerlessHandler>(PlatformServerlessHandler)!;
|
|
141
|
+
handler = platformHandler.createHandler(token, propertyKey);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const $ctx = new ServerlessContext<ServerlessEvent>({
|
|
145
|
+
event,
|
|
146
|
+
context,
|
|
147
|
+
responseStream,
|
|
148
|
+
id: getRequestId(event, context),
|
|
149
|
+
endpoint: entity
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return handler($ctx);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const isBinary = entity.operation.response?.isBinary();
|
|
156
|
+
|
|
157
|
+
if (isBinary) {
|
|
158
|
+
return ServerlessResponseStream.streamifyResponse(wrappedHandler);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return (event: ServerlessEvent, context: Context) => {
|
|
162
|
+
return wrappedHandler(event, undefined, context);
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
protected async initRouter() {
|
|
167
|
+
if (!this._router) {
|
|
168
|
+
const {default: FindMyMay} = await import("find-my-way");
|
|
169
|
+
|
|
170
|
+
this._router = FindMyMay({
|
|
171
|
+
caseSensitive: false,
|
|
172
|
+
ignoreTrailingSlash: true
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
this.callbacks();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return this._router;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected createInjector(settings: any) {
|
|
182
|
+
injector().logger = $log;
|
|
183
|
+
injector().settings.set(settings);
|
|
184
|
+
|
|
185
|
+
// istanbul ignore next
|
|
186
|
+
if (constant("env") === Env.TEST && !settings?.logger?.level) {
|
|
187
|
+
$log.stop();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
protected async loadInjector() {
|
|
194
|
+
const container = createContainer();
|
|
195
|
+
|
|
196
|
+
setLoggerConfiguration();
|
|
197
|
+
|
|
198
|
+
await injector().load(container);
|
|
199
|
+
|
|
200
|
+
await $asyncEmit("$afterInit");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {catchAsyncError} from "@tsed/core";
|
|
2
|
+
import {DITest, Inject, Injectable} from "@tsed/di";
|
|
3
|
+
import {Unauthorized} from "@tsed/exceptions";
|
|
4
|
+
import {QueryParams} from "@tsed/platform-params";
|
|
5
|
+
import {JsonEntityStore} from "@tsed/schema";
|
|
6
|
+
import type {APIGatewayTokenAuthorizerEvent} from "aws-lambda";
|
|
7
|
+
|
|
8
|
+
import {ServerlessContext} from "../domain/ServerlessContext.js";
|
|
9
|
+
import {PlatformServerlessHandler} from "./PlatformServerlessHandler.js";
|
|
10
|
+
|
|
11
|
+
async function getPlatformServerlessHandlerFixture() {
|
|
12
|
+
const service = await DITest.invoke<PlatformServerlessHandler>(PlatformServerlessHandler);
|
|
13
|
+
return {
|
|
14
|
+
service
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Injectable()
|
|
19
|
+
class TimeslotsService {
|
|
20
|
+
get() {
|
|
21
|
+
return "test";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
export class TimeslotsLambdaController {
|
|
27
|
+
@Inject()
|
|
28
|
+
protected timeslotsService: TimeslotsService;
|
|
29
|
+
|
|
30
|
+
get(@QueryParams("start_date") startDate: Date, @QueryParams("end_date") endDate: Date) {
|
|
31
|
+
return {
|
|
32
|
+
value: this.timeslotsService.get(),
|
|
33
|
+
startDate,
|
|
34
|
+
endDate
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
throwError(@QueryParams("start_date") startDate: Date, @QueryParams("end_date") endDate: Date) {
|
|
39
|
+
throw new Unauthorized("Unauthorized");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
describe("PlatformServerlessHandler", () => {
|
|
44
|
+
beforeEach(() => DITest.create({}));
|
|
45
|
+
afterEach(() => DITest.reset());
|
|
46
|
+
|
|
47
|
+
it("should call lambda provider and return http response", async () => {
|
|
48
|
+
const {service} = await getPlatformServerlessHandlerFixture();
|
|
49
|
+
|
|
50
|
+
const endpoint = JsonEntityStore.fromMethod(TimeslotsLambdaController, "get");
|
|
51
|
+
const $ctx = new ServerlessContext({
|
|
52
|
+
event: {
|
|
53
|
+
httpMethod: "GET",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json"
|
|
56
|
+
}
|
|
57
|
+
} as any,
|
|
58
|
+
context: {} as any,
|
|
59
|
+
endpoint
|
|
60
|
+
} as any);
|
|
61
|
+
|
|
62
|
+
const handler = service.createHandler(TimeslotsLambdaController, "get");
|
|
63
|
+
const result = await handler($ctx);
|
|
64
|
+
|
|
65
|
+
expect(result).toEqual({
|
|
66
|
+
body: '{"value":"test"}',
|
|
67
|
+
headers: {
|
|
68
|
+
"content-type": "application/json"
|
|
69
|
+
},
|
|
70
|
+
isBase64Encoded: false,
|
|
71
|
+
statusCode: 200
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
it("shouldn't throw error and return http response", async () => {
|
|
75
|
+
const {service} = await getPlatformServerlessHandlerFixture();
|
|
76
|
+
|
|
77
|
+
const endpoint = JsonEntityStore.fromMethod(TimeslotsLambdaController, "throwError");
|
|
78
|
+
const $ctx = new ServerlessContext({
|
|
79
|
+
event: {
|
|
80
|
+
httpMethod: "GET",
|
|
81
|
+
headers: {
|
|
82
|
+
"Content-Type": "application/json"
|
|
83
|
+
}
|
|
84
|
+
} as any,
|
|
85
|
+
context: {} as any,
|
|
86
|
+
endpoint,
|
|
87
|
+
injector: DITest.injector
|
|
88
|
+
} as any);
|
|
89
|
+
|
|
90
|
+
const handler = service.createHandler(TimeslotsLambdaController, "throwError");
|
|
91
|
+
|
|
92
|
+
const result = await handler($ctx);
|
|
93
|
+
|
|
94
|
+
expect(result).toEqual({
|
|
95
|
+
body: '{"name":"UNAUTHORIZED","message":"Unauthorized","status":401,"errors":[]}',
|
|
96
|
+
headers: {
|
|
97
|
+
"content-type": "application/json"
|
|
98
|
+
},
|
|
99
|
+
isBase64Encoded: false,
|
|
100
|
+
statusCode: 401
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
it("should call lambda provider and return raw response when isn't a http event", async () => {
|
|
104
|
+
const {service} = await getPlatformServerlessHandlerFixture();
|
|
105
|
+
|
|
106
|
+
const endpoint = JsonEntityStore.fromMethod(TimeslotsLambdaController, "get");
|
|
107
|
+
const $ctx = new ServerlessContext({
|
|
108
|
+
event: {
|
|
109
|
+
type: "TOKEN",
|
|
110
|
+
methodArn: "",
|
|
111
|
+
authorizationToken: ""
|
|
112
|
+
} as APIGatewayTokenAuthorizerEvent,
|
|
113
|
+
context: {} as any,
|
|
114
|
+
endpoint
|
|
115
|
+
} as any);
|
|
116
|
+
|
|
117
|
+
const handler = service.createHandler(TimeslotsLambdaController, "get");
|
|
118
|
+
const result = await handler($ctx);
|
|
119
|
+
|
|
120
|
+
expect(result).toEqual({
|
|
121
|
+
value: "test"
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
it("should throw error when isn't a http event", async () => {
|
|
125
|
+
const {service} = await getPlatformServerlessHandlerFixture();
|
|
126
|
+
|
|
127
|
+
const endpoint = JsonEntityStore.fromMethod(TimeslotsLambdaController, "throwError");
|
|
128
|
+
const $ctx = new ServerlessContext({
|
|
129
|
+
event: {
|
|
130
|
+
type: "TOKEN",
|
|
131
|
+
methodArn: "",
|
|
132
|
+
authorizationToken: ""
|
|
133
|
+
} as APIGatewayTokenAuthorizerEvent,
|
|
134
|
+
context: {} as any,
|
|
135
|
+
endpoint,
|
|
136
|
+
injector: DITest.injector
|
|
137
|
+
} as any);
|
|
138
|
+
|
|
139
|
+
const handler = service.createHandler(TimeslotsLambdaController, "throwError");
|
|
140
|
+
|
|
141
|
+
const result = await catchAsyncError<Error>(() => handler($ctx));
|
|
142
|
+
|
|
143
|
+
expect(result?.message).toEqual("Unauthorized");
|
|
144
|
+
});
|
|
145
|
+
});
|