/
Server.ts
154 lines (130 loc) · 5.47 KB
/
Server.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import createAPI, { API } from "lambda-api"
import { Container } from "inversify"
import { ApiRequest } from "../model/ApiRequest"
import { ApiResponse } from "../model/ApiResponse"
import { AppConfig } from "../model/AppConfig"
import { EndpointInfo } from "../model/reflection/EndpointInfo"
import { ILogger } from "../util/logging/ILogger"
import { LogFactory } from "../util/logging/LogFactory"
import { timed } from "../util/timed"
import { Endpoint } from "./Endpoint"
import { MiddlewareRegistry } from "./MiddlewareRegistry"
import { OpenApiGenerator, OpenApiFormat } from "./open-api/OpenApiGenerator"
import { ControllerLoader } from "./reflection/ControllerLoader"
import { DecoratorRegistry } from "./reflection/DecoratorRegistry"
/**
* Server that discovers routes using decorators on controller
* classes and methods. Processing of requests is preformed by the
* `lambda-api` package.
*/
export class Server {
private readonly logFactory: LogFactory
private readonly logger: ILogger
private readonly api: API
private readonly _middlewareRegistry: MiddlewareRegistry
private readonly openApiGenerator?: OpenApiGenerator
public get middlewareRegistry() {
return this._middlewareRegistry
}
/**
* Create a new server.
*
* @param appContainer Application container to use to build containers.
* @param appConfig Application config to pass to `lambda-api`.
*/
public constructor(private appContainer: Container, private appConfig: AppConfig) {
this.logFactory = new LogFactory(appConfig)
this.logger = this.logFactory.getLogger(Server)
// ensure decorator registry has the right log level (now that we know the app config)
DecoratorRegistry.setLogger(this.logFactory)
this.api = createAPI(appConfig)
this._middlewareRegistry = new MiddlewareRegistry(this.logFactory)
if (this.appConfig.openApi && this.appConfig.openApi.enabled) {
this.openApiGenerator = new OpenApiGenerator(this.appConfig, this._middlewareRegistry, this.logFactory)
}
}
/**
* Configure the `API` instance from the `lambda-api`
* package.
*
* @param handler Function that takes an `API` instance as a parameter.
*/
public configure(handler: (this: void, api: API) => void) {
handler(this.api)
}
/**
* Scans the specified path for javascript files and loads these into
* the current runtime. Importing the files will invoke the decorators
* declared within them. Note: this scans only the top level files.
*
* API decorators register controllers, endpoints, configuration and middleware.
* A series of endpoints are built using the decorator components and registered
* with the `lambda-api` package routing engine.
*
* Controllers and error interceptors registered by decorators are built using
* an IOC container, which allows dependency injection.
*
* OpenAPI endpoints will be registered here, if they are enabled in the app
* config by setting the `openApi.enabled` flag to true.
*
* This method must be called before invoking the `processEvent` method.
*
* @param controllersPath Path to the directory containing controller `js` files.
*/
@timed
public async discoverAndBuildRoutes(controllersPath: string) {
this.logger.debug("Loading controllers from path: %s", controllersPath)
await ControllerLoader.loadControllers(controllersPath, this.logFactory)
if (this.openApiGenerator) {
this.registerOpenApiEndpoints()
}
for (let endpointKey in DecoratorRegistry.Endpoints) {
if (!DecoratorRegistry.Endpoints.hasOwnProperty(endpointKey)) {
continue
}
this.registerEndpoint(DecoratorRegistry.Endpoints[endpointKey])
}
}
private registerOpenApiEndpoints() {
this.logger.info("Registering OpenAPI endpoints")
this.registerOpenApiEndpoint("json")
this.registerOpenApiEndpoint("yml")
}
private registerOpenApiEndpoint(format: OpenApiFormat) {
let specEndpoint = new EndpointInfo(
`internal__openapi::${format}`,
async () => await this.openApiGenerator.exportOpenApiSpec(format)
)
specEndpoint.path = `/open-api.${format}`
specEndpoint.httpMethod = "GET"
specEndpoint.noAuth = !this.appConfig.openApi.useAuthentication
specEndpoint.produces = `application/${format}`
this.registerEndpoint(specEndpoint)
}
private registerEndpoint(endpointInfo: EndpointInfo) {
let apiEndpoint = new Endpoint(
endpointInfo,
c => this.appContainer.get(c),
ei => this.appContainer.get(ei),
this._middlewareRegistry,
this.logFactory
)
apiEndpoint.register(this.api)
}
/**
* Takes an API request passed in by AWS Lambda and processes
* it using the `lambda-api` package.
*
* @param request API Gateway or ALB request.
* @param context Request context.
* @returns The response.
*/
@timed
public async processEvent(request: ApiRequest, context: any): Promise<ApiResponse> {
let event: any = request
this.logger.info("Processing API request event for path: %s", request.path)
this.logger.debug("Event data: %j", event)
this.logger.debug("Event context: %j", context)
return await this.api.run(event, context)
}
}