@tsed/platform-serverless 7.0.0-alpha.2 → 7.0.0-beta.1
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/jest.config.js +4 -4
- package/lib/cjs/builder/PlatformServerless.js +156 -0
- package/lib/cjs/builder/PlatformServerless.js.map +1 -0
- package/lib/cjs/builder/PlatformServerlessHandler.js +93 -0
- package/lib/cjs/builder/PlatformServerlessHandler.js.map +1 -0
- package/lib/cjs/domain/ServerlessContext.js +26 -0
- package/lib/cjs/domain/ServerlessContext.js.map +1 -0
- package/lib/cjs/domain/ServerlessRequest.js +80 -0
- package/lib/cjs/domain/ServerlessRequest.js.map +1 -0
- package/lib/cjs/domain/ServerlessResponse.js +193 -0
- package/lib/cjs/domain/ServerlessResponse.js.map +1 -0
- package/lib/cjs/index.js +22 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/utils/getRequestId.js +13 -0
- package/lib/cjs/utils/getRequestId.js.map +1 -0
- package/lib/cjs/utils/setResponseHeaders.js +35 -0
- package/lib/cjs/utils/setResponseHeaders.js.map +1 -0
- package/lib/esm/builder/PlatformServerless.js +133 -0
- package/lib/esm/builder/PlatformServerless.js.map +1 -0
- package/lib/esm/builder/PlatformServerlessHandler.js +90 -0
- package/lib/esm/builder/PlatformServerlessHandler.js.map +1 -0
- package/lib/esm/domain/ServerlessContext.js +22 -0
- package/lib/esm/domain/ServerlessContext.js.map +1 -0
- package/lib/esm/domain/ServerlessRequest.js +76 -0
- package/lib/esm/domain/ServerlessRequest.js.map +1 -0
- package/lib/esm/domain/ServerlessResponse.js +189 -0
- package/lib/esm/domain/ServerlessResponse.js.map +1 -0
- package/lib/esm/index.js +10 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/utils/getRequestId.js +9 -0
- package/lib/esm/utils/getRequestId.js.map +1 -0
- package/lib/esm/utils/setResponseHeaders.js +31 -0
- package/lib/esm/utils/setResponseHeaders.js.map +1 -0
- package/lib/{builder → types/builder}/PlatformServerless.d.ts +14 -6
- package/lib/{builder → types/builder}/PlatformServerlessHandler.d.ts +2 -2
- package/lib/{domain → types/domain}/ServerlessContext.d.ts +0 -0
- package/lib/{domain → types/domain}/ServerlessRequest.d.ts +0 -0
- package/lib/{domain → types/domain}/ServerlessResponse.d.ts +1 -1
- package/lib/{index.d.ts → types/index.d.ts} +0 -1
- package/lib/{utils → types/utils}/getRequestId.d.ts +0 -0
- package/lib/{utils → types/utils}/setResponseHeaders.d.ts +0 -0
- package/package.json +20 -18
- package/readme.md +269 -52
- package/tsconfig.compile.esm.json +23 -0
- package/lib/index.js +0 -987
- package/lib/index.js.map +0 -1
- package/lib/index.modern.js +0 -904
- package/lib/index.modern.js.map +0 -1
- package/lib/services/PlatformServerlessTest.d.ts +0 -41
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ServerlessResponse.js","sourceRoot":"","sources":["../../../src/domain/ServerlessResponse.ts"],"names":[],"mappings":"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","sourcesContent":["import {getValue} from \"@tsed/core\";\nimport {getStatusMessage} from \"@tsed/schema\";\nimport type {APIGatewayEventDefaultAuthorizerContext, APIGatewayProxyEventBase, Context} from \"aws-lambda\";\nimport encodeUrl from \"encodeurl\";\nimport * as mime from \"mime\";\nimport type {ServerlessRequest} from \"./ServerlessRequest\";\n\nexport interface ServerlessResponseOptions {\n event: APIGatewayProxyEventBase<APIGatewayEventDefaultAuthorizerContext>;\n context: Context;\n request: ServerlessRequest;\n}\n\nexport type HeaderValue = boolean | number | string;\n\n/**\n * @platform\n */\nexport class ServerlessResponse {\n readonly event: APIGatewayProxyEventBase<APIGatewayEventDefaultAuthorizerContext>;\n readonly request: ServerlessRequest;\n\n #status: number = 200;\n #body: any = undefined;\n #headers: Record<string, HeaderValue> = {};\n #locals: Record<string, any> = {};\n #isHeadersSent = false;\n #endListeners: Function[] = [];\n\n constructor({event, request}: ServerlessResponseOptions) {\n this.event = event;\n this.request = request;\n }\n\n /**\n * Get the current statusCode\n */\n get statusCode() {\n return this.#status;\n }\n\n set statusCode(status) {\n this.#status = status;\n }\n\n /**\n * An object that contains response local variables scoped to the request, and therefore available only to the view(s) rendered during that request / response cycle (if any). Otherwise, this property is identical to app.locals.\n *\n * This property is useful for exposing request-level information such as the request path name, authenticated user, user settings, and so on.\n */\n get locals() {\n return this.#locals;\n }\n\n get isHeadersSent() {\n return this.#isHeadersSent;\n }\n\n getStatus() {\n return this.#status;\n }\n\n /**\n * Sets the HTTP status for the response.\n *\n * @param status\n */\n status(status: number) {\n this.#status = status;\n return this;\n }\n\n set(name: string, value: any) {\n return this.setHeader(name, value);\n }\n\n /**\n * Returns the HTTP response header specified by field. The match is case-insensitive.\n *\n * ```typescript\n * response.get('Content-Type') // => \"text/plain\"\n * ```\n *\n * @param name\n */\n get(name: string): string | number | boolean | undefined {\n return getValue(this.#headers, name.toLowerCase());\n }\n\n getHeaders(): Record<string, HeaderValue> {\n return {\n ...this.#headers\n };\n }\n\n hasStatus() {\n return this.#status !== 200;\n }\n\n /**\n * Set header `field` to `val`, or pass\n * an object of header fields.\n *\n * Examples:\n * ```typescript\n * response.setHeaders({ Accept: 'text/plain', 'X-API-Key': 'tobi' });\n * ```\n *\n * Aliased as `res.header()`.\n */\n setHeaders(headers: Record<string, HeaderValue>) {\n // apply headers\n Object.entries(headers).forEach(([key, item]) => {\n this.setHeader(key, item);\n });\n\n return this;\n }\n\n setHeader(key: string, item: HeaderValue | null | undefined) {\n if (item !== null && item !== undefined) {\n key = key.toLowerCase();\n\n if (key === \"location\") {\n // \"back\" is an alias for the referrer\n if (item === \"back\") {\n item = this.request.get(\"Referrer\") || \"/\";\n }\n\n item = encodeUrl(String(item));\n }\n\n this.#headers = {\n ...this.#headers,\n [key.toLowerCase()]: item as any\n };\n }\n\n return this;\n }\n\n /**\n * Set `Content-Type` response header with `type` through `mime.lookup()`\n * when it does not contain \"/\", or set the Content-Type to `type` otherwise.\n *\n * Examples:\n *\n * res.type('.html');\n * res.type('html');\n * res.type('json');\n * res.type('application/json');\n * res.type('png');\n */\n contentType(contentType: string) {\n this.setHeader(\"Content-Type\", mime.getType(contentType));\n\n return this;\n }\n\n contentLength(length: number) {\n this.setHeader(\"Content-Length\", length);\n return this;\n }\n\n getContentLength() {\n if (this.get(\"Content-Length\")) {\n return parseInt(this.get(\"Content-Length\") as any, 10) || 0;\n }\n }\n\n getContentType() {\n return String(this.get(\"Content-Type\") || \"\").split(\";\")[0];\n }\n\n /**\n * Redirects to the URL derived from the specified path, with specified status, a positive integer that corresponds to an [HTTP status code](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html).\n * If not specified, status defaults to `302 Found`.\n *\n * @param status\n * @param url\n */\n redirect(status: number, url: string) {\n // Set location header\n const address = this.location(url).get(\"Location\");\n const body = `${getStatusMessage(status)}. Redirecting to ${address}`;\n\n this.status(status).set(\"Content-Length\", Buffer.byteLength(body)).body(body);\n\n return this;\n }\n\n /**\n * Sets the response Location HTTP header to the specified path parameter.\n *\n * @param url\n */\n location(url: string) {\n return this.set(\"Location\", url);\n }\n\n /**\n * Send any data to your consumer.\n *\n * This method accept a ReadableStream, a plain object, boolean, string, number, null and undefined data.\n * It choose the better way to send the data.\n *\n * @param body\n */\n body(body: any) {\n this.#body = body;\n\n return this;\n }\n\n getBody() {\n return this.#body;\n }\n\n isDone(): boolean {\n return this.#isHeadersSent;\n }\n\n onEnd(cb: Function) {\n this.#endListeners.push(cb);\n\n return this;\n }\n\n destroy() {\n this.#endListeners.forEach((cb: Function) => cb());\n this.#isHeadersSent = true;\n }\n}\n"]}
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "@tsed/di";
|
|
2
|
+
export { Get, Post, Put, Delete, Head, Patch, Options, Route as Lambda } from "@tsed/schema";
|
|
3
|
+
export * from "@tsed/platform-params";
|
|
4
|
+
export * from "./builder/PlatformServerlessHandler";
|
|
5
|
+
export * from "./builder/PlatformServerless";
|
|
6
|
+
// decorators
|
|
7
|
+
export * from "./domain/ServerlessContext";
|
|
8
|
+
export * from "./domain/ServerlessResponse";
|
|
9
|
+
export * from "./domain/ServerlessResponse";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,MAAM,EAAC,MAAM,cAAc,CAAC;AAC3F,cAAc,uBAAuB,CAAC;AAEtC,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAE7C,aAAa;AACb,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,6BAA6B,CAAC","sourcesContent":["export * from \"@tsed/di\";\nexport {Get, Post, Put, Delete, Head, Patch, Options, Route as Lambda} from \"@tsed/schema\";\nexport * from \"@tsed/platform-params\";\n\nexport * from \"./builder/PlatformServerlessHandler\";\nexport * from \"./builder/PlatformServerless\";\n\n// decorators\nexport * from \"./domain/ServerlessContext\";\nexport * from \"./domain/ServerlessResponse\";\nexport * from \"./domain/ServerlessResponse\";\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { v4 } from "uuid";
|
|
2
|
+
export function getRequestId(event, context) {
|
|
3
|
+
var _a;
|
|
4
|
+
if ((event === null || event === void 0 ? void 0 : event.headers) && event.headers["x-request-id"]) {
|
|
5
|
+
return event.headers["x-request-id"];
|
|
6
|
+
}
|
|
7
|
+
return ((_a = event === null || event === void 0 ? void 0 : event.requestContext) === null || _a === void 0 ? void 0 : _a.requestId) || (context === null || context === void 0 ? void 0 : context.awsRequestId) || v4().replace(/-/gi, "");
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=getRequestId.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getRequestId.js","sourceRoot":"","sources":["../../../src/utils/getRequestId.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,EAAE,EAAC,MAAM,MAAM,CAAC;AAExB,MAAM,UAAU,YAAY,CAAC,KAAwE,EAAE,OAAgB;;IACrH,IAAI,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,OAAO,KAAI,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE;QACnD,OAAO,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;KACtC;IAED,OAAO,CAAA,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,cAAc,0CAAE,SAAS,MAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,YAAY,CAAA,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC9F,CAAC","sourcesContent":["import {APIGatewayEventDefaultAuthorizerContext, APIGatewayProxyEventBase, Context} from \"aws-lambda\";\nimport {v4} from \"uuid\";\n\nexport function getRequestId(event: APIGatewayProxyEventBase<APIGatewayEventDefaultAuthorizerContext>, context: Context) {\n if (event?.headers && event.headers[\"x-request-id\"]) {\n return event.headers[\"x-request-id\"];\n }\n\n return event?.requestContext?.requestId || context?.awsRequestId || v4().replace(/-/gi, \"\");\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function mergeHeaders(specHeaders, headers) {
|
|
2
|
+
return Object.entries(specHeaders).reduce((headers, [key, item]) => {
|
|
3
|
+
key = key.toLowerCase();
|
|
4
|
+
return {
|
|
5
|
+
...headers,
|
|
6
|
+
[key]: headers[key] === undefined ? String(item.example) : headers[key]
|
|
7
|
+
};
|
|
8
|
+
}, headers);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* @ignore
|
|
12
|
+
*/
|
|
13
|
+
export function setResponseHeaders(ctx) {
|
|
14
|
+
const { response, endpoint } = ctx;
|
|
15
|
+
const operation = endpoint.operation;
|
|
16
|
+
if (response.isDone()) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (!response.hasStatus()) {
|
|
20
|
+
// apply status only if the isn't already modified
|
|
21
|
+
response.status(operation.getStatus());
|
|
22
|
+
}
|
|
23
|
+
const statusCode = response.statusCode;
|
|
24
|
+
const headers = operation.getHeadersOf(response.statusCode);
|
|
25
|
+
const mergedHeaders = mergeHeaders(headers, response.getHeaders());
|
|
26
|
+
response.setHeaders(mergedHeaders);
|
|
27
|
+
if (operation.isRedirection(statusCode)) {
|
|
28
|
+
response.redirect(statusCode, String(mergedHeaders["location"]));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=setResponseHeaders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setResponseHeaders.js","sourceRoot":"","sources":["../../../src/utils/setResponseHeaders.ts"],"names":[],"mappings":"AAIA,SAAS,YAAY,CAAC,WAA2D,EAAE,OAAoC;IACrH,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE;QACjE,GAAG,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QACxB,OAAO;YACL,GAAG,OAAO;YACV,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;SACxE,CAAC;IACJ,CAAC,EAAE,OAAO,CAAC,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAsB;IACvD,MAAM,EAAC,QAAQ,EAAE,QAAQ,EAAC,GAAG,GAAG,CAAC;IACjC,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAU,CAAC;IAEtC,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE;QACrB,OAAO;KACR;IAED,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE;QACzB,kDAAkD;QAClD,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC;KACxC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC;IACvC,MAAM,OAAO,GAAG,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC5D,MAAM,aAAa,GAAG,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAEnE,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAEnC,IAAI,SAAS,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE;QACvC,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;KAClE;AACH,CAAC","sourcesContent":["import {JsonHeader} from \"@tsed/schema\";\nimport {ServerlessContext} from \"../domain/ServerlessContext\";\nimport {HeaderValue} from \"../domain/ServerlessResponse\";\n\nfunction mergeHeaders(specHeaders: Record<string, JsonHeader & {example: string}>, headers: Record<string, HeaderValue>) {\n return Object.entries(specHeaders).reduce((headers, [key, item]) => {\n key = key.toLowerCase();\n return {\n ...headers,\n [key]: headers[key] === undefined ? String(item.example) : headers[key]\n };\n }, headers);\n}\n\n/**\n * @ignore\n */\nexport function setResponseHeaders(ctx: ServerlessContext) {\n const {response, endpoint} = ctx;\n const operation = endpoint.operation!;\n\n if (response.isDone()) {\n return;\n }\n\n if (!response.hasStatus()) {\n // apply status only if the isn't already modified\n response.status(operation.getStatus());\n }\n\n const statusCode = response.statusCode;\n const headers = operation.getHeadersOf(response.statusCode);\n const mergedHeaders = mergeHeaders(headers, response.getHeaders());\n\n response.setHeaders(mergedHeaders);\n\n if (operation.isRedirection(statusCode)) {\n response.redirect(statusCode, String(mergedHeaders[\"location\"]));\n }\n}\n"]}
|
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
import { Type } from "@tsed/core";
|
|
2
2
|
import { InjectorService } from "@tsed/di";
|
|
3
|
-
import type {
|
|
3
|
+
import type { Instance } from "find-my-way";
|
|
4
|
+
import type { Handler } from "aws-lambda";
|
|
5
|
+
export interface PlatformServerlessSettings extends Partial<TsED.Configuration> {
|
|
6
|
+
lambda?: Type[];
|
|
7
|
+
}
|
|
4
8
|
/**
|
|
5
9
|
* @platform
|
|
6
10
|
*/
|
|
7
11
|
export declare class PlatformServerless {
|
|
8
|
-
#private;
|
|
9
12
|
readonly name: string;
|
|
13
|
+
private _injector;
|
|
14
|
+
private _router;
|
|
15
|
+
private _promise;
|
|
10
16
|
get injector(): InjectorService;
|
|
11
17
|
get settings(): TsED.Configuration & import("@tsed/di").DIConfiguration;
|
|
12
|
-
get promise(): Promise<
|
|
18
|
+
get promise(): Promise<any>;
|
|
13
19
|
static bootstrap(settings?: Partial<TsED.Configuration> & {
|
|
14
20
|
lambda?: Type[];
|
|
15
21
|
}): PlatformServerless;
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
handler(): Handler;
|
|
23
|
+
callbacks(tokens?: Type | Type[], callbacks?: any): Record<string, Handler>;
|
|
18
24
|
ready(): Promise<void>;
|
|
19
25
|
stop(): Promise<void>;
|
|
20
|
-
|
|
26
|
+
init(): Promise<any>;
|
|
27
|
+
protected callback(token: Type<any>, propertyKey: string): Handler;
|
|
28
|
+
protected initRouter(): Promise<Instance<any>>;
|
|
21
29
|
protected createInjector(settings: any): this;
|
|
22
30
|
protected loadInjector(): Promise<void>;
|
|
23
31
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { InjectorService, TokenProvider } from "@tsed/di";
|
|
2
|
-
import { PlatformExceptions } from "@tsed/platform-exceptions";
|
|
3
2
|
import { PlatformParams } from "@tsed/platform-params";
|
|
3
|
+
import type { PlatformExceptions } from "@tsed/platform-exceptions";
|
|
4
4
|
import { ServerlessContext } from "../domain/ServerlessContext";
|
|
5
5
|
export declare class PlatformServerlessHandler {
|
|
6
6
|
protected injector: InjectorService;
|
|
7
7
|
protected params: PlatformParams;
|
|
8
|
-
protected
|
|
8
|
+
protected exceptionsManager: Promise<PlatformExceptions>;
|
|
9
9
|
createHandler(token: TokenProvider, propertyKey: string | symbol): ($ctx: ServerlessContext) => Promise<{
|
|
10
10
|
statusCode: number;
|
|
11
11
|
body: any;
|
|
File without changes
|
|
File without changes
|
|
@@ -25,6 +25,7 @@ export declare class ServerlessResponse {
|
|
|
25
25
|
* This property is useful for exposing request-level information such as the request path name, authenticated user, user settings, and so on.
|
|
26
26
|
*/
|
|
27
27
|
get locals(): Record<string, any>;
|
|
28
|
+
get isHeadersSent(): boolean;
|
|
28
29
|
getStatus(): number;
|
|
29
30
|
/**
|
|
30
31
|
* Sets the HTTP status for the response.
|
|
@@ -101,5 +102,4 @@ export declare class ServerlessResponse {
|
|
|
101
102
|
isDone(): boolean;
|
|
102
103
|
onEnd(cb: Function): this;
|
|
103
104
|
destroy(): void;
|
|
104
|
-
isHeadersSent(): boolean;
|
|
105
105
|
}
|
|
@@ -3,7 +3,6 @@ export { Get, Post, Put, Delete, Head, Patch, Options, Route as Lambda } from "@
|
|
|
3
3
|
export * from "@tsed/platform-params";
|
|
4
4
|
export * from "./builder/PlatformServerlessHandler";
|
|
5
5
|
export * from "./builder/PlatformServerless";
|
|
6
|
-
export * from "./services/PlatformServerlessTest";
|
|
7
6
|
export * from "./domain/ServerlessContext";
|
|
8
7
|
export * from "./domain/ServerlessResponse";
|
|
9
8
|
export * from "./domain/ServerlessResponse";
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,34 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsed/platform-serverless",
|
|
3
|
-
"version": "7.0.0-
|
|
3
|
+
"version": "7.0.0-beta.1",
|
|
4
4
|
"description": "Platform serverless module for Ts.ED Framework",
|
|
5
5
|
"private": false,
|
|
6
6
|
"source": "./src/index.ts",
|
|
7
|
-
"main": "./lib/index.js",
|
|
8
|
-
"typings": "./lib/index.d.ts",
|
|
7
|
+
"main": "./lib/cjs/index.js",
|
|
8
|
+
"typings": "./lib/types/index.d.ts",
|
|
9
9
|
"exports": {
|
|
10
|
-
"
|
|
11
|
-
"
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"import": "./lib/esm/index.js",
|
|
12
|
+
"require": "./lib/cjs/index.js",
|
|
13
|
+
"default": "./lib/esm/index.js"
|
|
12
14
|
},
|
|
13
15
|
"scripts": {
|
|
14
|
-
"build": "
|
|
16
|
+
"build": "yarn run build:esm && yarn run build:cjs",
|
|
17
|
+
"build:cjs": "tsc --build tsconfig.compile.json",
|
|
18
|
+
"build:esm": "tsc --build tsconfig.compile.esm.json",
|
|
15
19
|
"test": "cross-env NODE_ENV=test jest"
|
|
16
20
|
},
|
|
17
21
|
"dependencies": {
|
|
18
|
-
"@tsed/core": "7.0.0-
|
|
19
|
-
"@tsed/di": "7.0.0-
|
|
20
|
-
"@tsed/exceptions": "7.0.0-
|
|
21
|
-
"@tsed/json-mapper": "7.0.0-
|
|
22
|
-
"@tsed/logger": "^
|
|
23
|
-
"@tsed/platform-exceptions": "7.0.0-
|
|
24
|
-
"@tsed/platform-params": "7.0.0-
|
|
25
|
-
"@tsed/schema": "7.0.0-
|
|
22
|
+
"@tsed/core": "7.0.0-beta.1",
|
|
23
|
+
"@tsed/di": "7.0.0-beta.1",
|
|
24
|
+
"@tsed/exceptions": "7.0.0-beta.1",
|
|
25
|
+
"@tsed/json-mapper": "7.0.0-beta.1",
|
|
26
|
+
"@tsed/logger": "^6.0.0",
|
|
27
|
+
"@tsed/platform-exceptions": "7.0.0-beta.1",
|
|
28
|
+
"@tsed/platform-params": "7.0.0-beta.1",
|
|
29
|
+
"@tsed/schema": "7.0.0-beta.1",
|
|
26
30
|
"encodeurl": "^1.0.2",
|
|
27
31
|
"mime": "^2.5.2",
|
|
28
32
|
"tslib": "2.2.0",
|
|
29
33
|
"uuid": "^8.3.2"
|
|
30
34
|
},
|
|
31
35
|
"devDependencies": {
|
|
36
|
+
"@tsed/platform-serverless-testing": "7.0.0-beta.1",
|
|
32
37
|
"@types/aws-lambda": "^8.10.83",
|
|
33
38
|
"@types/encodeurl": "^1.0.0",
|
|
34
39
|
"@types/mime": "^2.0.3"
|
|
@@ -40,8 +45,5 @@
|
|
|
40
45
|
},
|
|
41
46
|
"homepage": "https://github.com/tsedio/tsed/tree/alpha/packages/platform/platform-serverless",
|
|
42
47
|
"author": "Romain Lenzotti",
|
|
43
|
-
"license": "MIT"
|
|
44
|
-
"publishConfig": {
|
|
45
|
-
"tag": "alpha"
|
|
46
|
-
}
|
|
48
|
+
"license": "MIT"
|
|
47
49
|
}
|
package/readme.md
CHANGED
|
@@ -36,16 +36,94 @@ 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
|
-
|
|
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
|
+
```bash
|
|
119
|
+
yarn add @tsed/platform-serverless serverless serverless-offline
|
|
120
|
+
yarn add -D @types/aws-lambda
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Npm:
|
|
124
|
+
```bash
|
|
125
|
+
npm install --save @tsed/platform-serverless serverless serverless-offline
|
|
126
|
+
npm install --save-dev @types/aws-lambda
|
|
49
127
|
```
|
|
50
128
|
|
|
51
129
|
## Configuration
|
|
@@ -53,13 +131,13 @@ npm install --save @tsed/platform-serverless
|
|
|
53
131
|
In the `src/lambda` create a new Lambda class:
|
|
54
132
|
|
|
55
133
|
```typescript
|
|
56
|
-
import {
|
|
57
|
-
import {Returns, Summary
|
|
134
|
+
import {Controller, Inject} from "@tsed/di";
|
|
135
|
+
import {Get, Returns, Summary} from "@tsed/schema";
|
|
58
136
|
import {QueryParams} from "@tsed/platform-params"; // /!\ don't import decorators from @tsed/common
|
|
59
137
|
import {TimeslotsService} from "../services/TimeslotsService";
|
|
60
138
|
import {TimeslotModel} from "../models/TimeslotModel";
|
|
61
139
|
|
|
62
|
-
@
|
|
140
|
+
@Controller("/timeslots")
|
|
63
141
|
export class TimeslotsLambda {
|
|
64
142
|
@Inject()
|
|
65
143
|
protected timeslotsService: TimeslotsService;
|
|
@@ -71,15 +149,15 @@ export class TimeslotsLambda {
|
|
|
71
149
|
return this.timeslotsService.find({
|
|
72
150
|
dateStart,
|
|
73
151
|
dateEnd
|
|
74
|
-
})
|
|
152
|
+
});
|
|
75
153
|
}
|
|
76
154
|
}
|
|
77
155
|
```
|
|
78
156
|
|
|
79
|
-
Create new handler.ts to expose your lambda:
|
|
157
|
+
Create new `handler.ts` to expose your lambda:
|
|
80
158
|
|
|
81
159
|
```typescript
|
|
82
|
-
import {PlatformServerless} from "@tsed/serverless";
|
|
160
|
+
import {PlatformServerless} from "@tsed/platform-serverless";
|
|
83
161
|
import {TimeslotsLambda} from "./lambda/TimeslotsLambda";
|
|
84
162
|
|
|
85
163
|
const platform = PlatformServerless.bootstrap({
|
|
@@ -93,70 +171,124 @@ const platform = PlatformServerless.bootstrap({
|
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
177
|
+
service: timeslots
|
|
178
|
+
|
|
179
|
+
frameworkVersion: '2'
|
|
180
|
+
|
|
181
|
+
provider:
|
|
182
|
+
name: aws
|
|
183
|
+
runtime: nodejs14.x
|
|
184
|
+
lambdaHashingVersion: '20201221'
|
|
185
|
+
|
|
186
|
+
plugins:
|
|
187
|
+
- serverless-offline
|
|
188
|
+
|
|
189
|
+
functions:
|
|
190
|
+
timeslots:
|
|
191
|
+
handler: dist/handler.getTimeslots
|
|
192
|
+
events:
|
|
193
|
+
- http:
|
|
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:
|
|
111
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
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
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
|
|
112
247
|
service: timeslots
|
|
113
|
-
# app and org for use with dashboard.serverless.com
|
|
114
|
-
#app: your-app-name
|
|
115
|
-
#org: your-org-name
|
|
116
248
|
|
|
117
|
-
# You can pin your service to only deploy with a specific Serverless version
|
|
118
|
-
# Check out our docs for more details
|
|
119
249
|
frameworkVersion: '2'
|
|
120
250
|
|
|
121
251
|
provider:
|
|
122
252
|
name: aws
|
|
123
|
-
runtime:
|
|
124
|
-
lambdaHashingVersion: 20201221
|
|
253
|
+
runtime: nodejs14.x
|
|
254
|
+
lambdaHashingVersion: '20201221'
|
|
255
|
+
|
|
256
|
+
plugins:
|
|
257
|
+
- serverless-offline
|
|
125
258
|
|
|
126
259
|
functions:
|
|
127
|
-
|
|
128
|
-
handler: handler.
|
|
260
|
+
any:
|
|
261
|
+
handler: dist/handler.handler
|
|
129
262
|
events:
|
|
130
263
|
- http:
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
264
|
+
method: ANY
|
|
265
|
+
path: /
|
|
266
|
+
- http:
|
|
267
|
+
method: ANY
|
|
268
|
+
path: '{proxy+}'
|
|
134
269
|
```
|
|
135
270
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
In you lambda controller use Lambda decorators:
|
|
271
|
+
Then, edit the `handler.ts` and change the exported functions:
|
|
139
272
|
|
|
140
273
|
```typescript
|
|
141
|
-
import {
|
|
142
|
-
import {
|
|
143
|
-
import {QueryParams} from "@tsed/platform-params"; // /!\ 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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
protected timeslotsService: TimeslotsService;
|
|
277
|
+
const platform = PlatformServerless.bootstrap({
|
|
278
|
+
lambda: [TimeslotsLambda]
|
|
279
|
+
});
|
|
151
280
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
```
|
|
@@ -184,6 +316,91 @@ export class TimeslotsLambda {
|
|
|
184
316
|
}
|
|
185
317
|
```
|
|
186
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
|
+
};
|
|
343
|
+
}
|
|
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.call("getById")
|
|
368
|
+
.params({
|
|
369
|
+
id: "1"
|
|
370
|
+
})
|
|
371
|
+
.query({
|
|
372
|
+
start_date: new Date("2020-01-01"),
|
|
373
|
+
end_date: new Date("2020-01-10")
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
expect(response.statusCode).toEqual(200);
|
|
377
|
+
expect(response.headers).toEqual({
|
|
378
|
+
"x-request-id": "requestId",
|
|
379
|
+
"content-type": "application/json"
|
|
380
|
+
});
|
|
381
|
+
expect(JSON.parse(response.body)).toEqual({
|
|
382
|
+
id: "1",
|
|
383
|
+
endDate: "2020-01-10T00:00:00.000Z",
|
|
384
|
+
startDate: "2020-01-01T00:00:00.000Z"
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
describe("invoke using the router", () => {
|
|
390
|
+
it("should call getAll Lambda", async () => {
|
|
391
|
+
const response = await PlatformServerlessTest.request.get("/");
|
|
392
|
+
|
|
393
|
+
expect(response.statusCode).toEqual(200);
|
|
394
|
+
expect(response.headers).toEqual({
|
|
395
|
+
"x-request-id": "requestId",
|
|
396
|
+
"content-type": "application/json"
|
|
397
|
+
});
|
|
398
|
+
expect(JSON.parse(response.body)).toEqual([]);
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
187
404
|
## Contributors
|
|
188
405
|
Please read [contributing guidelines here](https://tsed.io/CONTRIBUTING.html).
|
|
189
406
|
|
|
@@ -0,0 +1,23 @@
|
|
|
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": [
|
|
12
|
+
"node_modules",
|
|
13
|
+
"test",
|
|
14
|
+
"lib",
|
|
15
|
+
"benchmark",
|
|
16
|
+
"coverage",
|
|
17
|
+
"spec",
|
|
18
|
+
"**/*.benchmark.ts",
|
|
19
|
+
"**/*.spec.ts",
|
|
20
|
+
"keys",
|
|
21
|
+
"jest.config.js"
|
|
22
|
+
]
|
|
23
|
+
}
|