UNPKG

4.86 kBPlain TextView Raw
1// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
2// Node module: @loopback/rest
3// This file is licensed under the MIT License.
4// License text available at https://opensource.org/licenses/MIT
5
6import {
7 Application,
8 Binding,
9 Component,
10 Constructor,
11 CoreBindings,
12 CoreTags,
13 createBindingFromClass,
14 inject,
15 ProviderMap,
16 Server,
17} from '@loopback/core';
18import {InvokeMiddlewareProvider} from '@loopback/express';
19import {createEmptyApiSpec} from '@loopback/openapi-v3';
20import {
21 JsonBodyParser,
22 RequestBodyParser,
23 StreamBodyParser,
24 TextBodyParser,
25 UrlEncodedBodyParser,
26} from './body-parsers';
27import {RawBodyParser} from './body-parsers/body-parser.raw';
28import {RestBindings, RestTags} from './keys';
29import {
30 FindRouteMiddlewareProvider,
31 FindRouteProvider,
32 InvokeMethodMiddlewareProvider,
33 InvokeMethodProvider,
34 LogErrorProvider,
35 ParseParamsMiddlewareProvider,
36 ParseParamsProvider,
37 RejectProvider,
38 SendProvider,
39 SendResponseMiddlewareProvider,
40} from './providers';
41import {
42 createBodyParserBinding,
43 RestServer,
44 RestServerConfig,
45} from './rest.server';
46import {ConsolidationEnhancer} from './spec-enhancers/consolidate.spec-enhancer';
47import {InfoSpecEnhancer} from './spec-enhancers/info.spec-enhancer';
48import {AjvFactoryProvider} from './validation/ajv-factory.provider';
49
50export class RestComponent implements Component {
51 providers: ProviderMap = {
52 [RestBindings.AJV_FACTORY.key]: AjvFactoryProvider,
53 };
54 /**
55 * Add built-in body parsers
56 */
57 bindings: Binding[] = [
58 ...createActionBindings(),
59 // FIXME(rfeng): We now register request body parsers in TRANSIENT scope
60 // so that they can be bound at application or server level
61 Binding.bind(RestBindings.REQUEST_BODY_PARSER).toClass(RequestBodyParser),
62 createBodyParserBinding(
63 JsonBodyParser,
64 RestBindings.REQUEST_BODY_PARSER_JSON,
65 ),
66 createBodyParserBinding(
67 TextBodyParser,
68 RestBindings.REQUEST_BODY_PARSER_TEXT,
69 ),
70 createBodyParserBinding(
71 UrlEncodedBodyParser,
72 RestBindings.REQUEST_BODY_PARSER_URLENCODED,
73 ),
74 createBodyParserBinding(
75 RawBodyParser,
76 RestBindings.REQUEST_BODY_PARSER_RAW,
77 ),
78 createBodyParserBinding(
79 StreamBodyParser,
80 RestBindings.REQUEST_BODY_PARSER_STREAM,
81 ),
82 createBindingFromClass(InfoSpecEnhancer),
83 createBindingFromClass(ConsolidationEnhancer),
84
85 ...getRestMiddlewareBindings(),
86 ];
87 servers: {
88 [name: string]: Constructor<Server>;
89 } = {
90 RestServer,
91 };
92
93 constructor(
94 @inject(CoreBindings.APPLICATION_INSTANCE) app: Application,
95 @inject(RestBindings.CONFIG) config?: RestComponentConfig,
96 ) {
97 // Register the `InvokeMiddleware` with default to `ACTION_MIDDLEWARE_CHAIN`
98 // to keep backward compatibility with action based sequence
99 const invokeMiddlewareActionBinding = createBindingFromClass(
100 InvokeMiddlewareProvider,
101 {
102 key: RestBindings.SequenceActions.INVOKE_MIDDLEWARE,
103 },
104 ).tag({[CoreTags.EXTENSION_POINT]: RestTags.ACTION_MIDDLEWARE_CHAIN});
105 app.add(invokeMiddlewareActionBinding);
106
107 // Register the `InvokeMiddleware` with default to `DEFAULT_MIDDLEWARE_CHAIN`
108 // for the middleware based sequence
109 const invokeMiddlewareServiceBinding = createBindingFromClass(
110 InvokeMiddlewareProvider,
111 {
112 key: RestBindings.INVOKE_MIDDLEWARE_SERVICE,
113 },
114 ).tag({[CoreTags.EXTENSION_POINT]: RestTags.REST_MIDDLEWARE_CHAIN});
115 app.add(invokeMiddlewareServiceBinding);
116
117 const apiSpec = createEmptyApiSpec();
118 // Merge the OpenAPI `servers` spec from the config into the empty one
119 if (config?.openApiSpec?.servers) {
120 Object.assign(apiSpec, {servers: config.openApiSpec.servers});
121 }
122 app.bind(RestBindings.API_SPEC).to(apiSpec);
123 }
124}
125
126function getRestMiddlewareBindings() {
127 return [
128 SendResponseMiddlewareProvider,
129 FindRouteMiddlewareProvider,
130 ParseParamsMiddlewareProvider,
131 InvokeMethodMiddlewareProvider,
132 ].map(cls => createBindingFromClass(cls));
133}
134
135function createActionBindings() {
136 const bindings: Binding[] = [];
137 const providers = {
138 [RestBindings.SequenceActions.LOG_ERROR.key]: LogErrorProvider,
139 [RestBindings.SequenceActions.FIND_ROUTE.key]: FindRouteProvider,
140 [RestBindings.SequenceActions.INVOKE_METHOD.key]: InvokeMethodProvider,
141 [RestBindings.SequenceActions.REJECT.key]: RejectProvider,
142 [RestBindings.SequenceActions.PARSE_PARAMS.key]: ParseParamsProvider,
143 [RestBindings.SequenceActions.SEND.key]: SendProvider,
144 };
145 for (const k in providers) {
146 bindings.push(createBindingFromClass(providers[k], {key: k}));
147 }
148 return bindings;
149}
150
151// TODO(kevin): Extend this interface def to include multiple servers?
152export type RestComponentConfig = RestServerConfig;