1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | Object.defineProperty(exports, "__esModule", { value: true });
|
7 | exports.createBodyParserBinding = exports.RestServer = void 0;
|
8 | const tslib_1 = require("tslib");
|
9 | const core_1 = require("@loopback/core");
|
10 | const express_1 = require("@loopback/express");
|
11 | const http_server_1 = require("@loopback/http-server");
|
12 | const openapi_v3_1 = require("@loopback/openapi-v3");
|
13 | const assert_1 = tslib_1.__importStar(require("assert"));
|
14 | const cors_1 = tslib_1.__importDefault(require("cors"));
|
15 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
16 | const express_2 = tslib_1.__importDefault(require("express"));
|
17 | const fs_1 = tslib_1.__importDefault(require("fs"));
|
18 | const js_yaml_1 = require("js-yaml");
|
19 | const lodash_1 = require("lodash");
|
20 | const strong_error_handler_1 = require("strong-error-handler");
|
21 | const body_parsers_1 = require("./body-parsers");
|
22 | const http_handler_1 = require("./http-handler");
|
23 | const keys_1 = require("./keys");
|
24 | const request_context_1 = require("./request-context");
|
25 | const router_1 = require("./router");
|
26 | const router_spec_1 = require("./router/router-spec");
|
27 | const sequence_1 = require("./sequence");
|
28 | const debug = (0, debug_1.default)('loopback:rest:server');
|
29 | const SequenceActions = keys_1.RestBindings.SequenceActions;
|
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 | let RestServer = class RestServer extends express_1.BaseMiddlewareRegistry {
|
58 |
|
59 | get OASEnhancer() {
|
60 | this._setupOASEnhancerIfNeeded();
|
61 | return this.oasEnhancerService;
|
62 | }
|
63 | get requestHandler() {
|
64 | if (this._requestHandler == null) {
|
65 | this._setupRequestHandlerIfNeeded();
|
66 | }
|
67 | return this._requestHandler;
|
68 | }
|
69 | get httpHandler() {
|
70 | this._setupHandlerIfNeeded();
|
71 | return this._httpHandler;
|
72 | }
|
73 | get listening() {
|
74 | return this._httpServer ? this._httpServer.listening : false;
|
75 | }
|
76 | get httpServer() {
|
77 | return this._httpServer;
|
78 | }
|
79 | |
80 |
|
81 |
|
82 |
|
83 |
|
84 | get url() {
|
85 | let serverUrl = this.rootUrl;
|
86 | if (!serverUrl)
|
87 | return serverUrl;
|
88 | serverUrl = serverUrl + (this._basePath || '');
|
89 | return serverUrl;
|
90 | }
|
91 | |
92 |
|
93 |
|
94 |
|
95 | get rootUrl() {
|
96 | var _a;
|
97 | return (_a = this._httpServer) === null || _a === void 0 ? void 0 : _a.url;
|
98 | }
|
99 | |
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 |
|
108 |
|
109 | constructor(app, config = {}) {
|
110 | var _a;
|
111 | super(app);
|
112 | |
113 |
|
114 |
|
115 | this._externalRoutes = new router_1.ExternalExpressRoutes();
|
116 | this.scope = core_1.BindingScope.SERVER;
|
117 | this.config = resolveRestServerConfig(config);
|
118 | this.bind(keys_1.RestBindings.PORT).to(this.config.port);
|
119 | this.bind(keys_1.RestBindings.HOST).to(config.host);
|
120 | this.bind(keys_1.RestBindings.PATH).to(config.path);
|
121 | this.bind(keys_1.RestBindings.PROTOCOL).to((_a = config.protocol) !== null && _a !== void 0 ? _a : 'http');
|
122 | this.bind(keys_1.RestBindings.HTTPS_OPTIONS).to(config);
|
123 | if (config.requestBodyParser) {
|
124 | this.bind(keys_1.RestBindings.REQUEST_BODY_PARSER_OPTIONS).to(config.requestBodyParser);
|
125 | }
|
126 | if (config.sequence) {
|
127 | this.sequence(config.sequence);
|
128 | }
|
129 | else {
|
130 | this.sequence(sequence_1.MiddlewareSequence);
|
131 | }
|
132 | if (config.router) {
|
133 | this.bind(keys_1.RestBindings.ROUTER_OPTIONS).to(config.router);
|
134 | }
|
135 | this.basePath(config.basePath);
|
136 | this.bind(keys_1.RestBindings.BASE_PATH).toDynamicValue(() => this._basePath);
|
137 | this.bind(keys_1.RestBindings.HANDLER).toDynamicValue(() => this.httpHandler);
|
138 | }
|
139 | _setupOASEnhancerIfNeeded() {
|
140 | if (this.oasEnhancerService != null)
|
141 | return;
|
142 | this.add((0, core_1.createBindingFromClass)(openapi_v3_1.OASEnhancerService, {
|
143 | key: openapi_v3_1.OASEnhancerBindings.OAS_ENHANCER_SERVICE,
|
144 | }));
|
145 | this.oasEnhancerService = this.getSync(openapi_v3_1.OASEnhancerBindings.OAS_ENHANCER_SERVICE);
|
146 | }
|
147 | _setupRequestHandlerIfNeeded() {
|
148 | if (this._expressApp != null)
|
149 | return;
|
150 | this._expressApp = (0, express_2.default)();
|
151 | this._applyExpressSettings();
|
152 | this._requestHandler = this._expressApp;
|
153 |
|
154 |
|
155 | this.expressMiddleware(cors_1.default, this.config.cors, {
|
156 | injectConfiguration: false,
|
157 | key: 'middleware.cors',
|
158 | group: sequence_1.RestMiddlewareGroups.CORS,
|
159 | }).apply((0, core_1.extensionFor)(keys_1.RestTags.REST_MIDDLEWARE_CHAIN, keys_1.RestTags.ACTION_MIDDLEWARE_CHAIN));
|
160 |
|
161 | this._setupOpenApiSpecEndpoints();
|
162 |
|
163 | this._expressApp.use(this._basePath, (req, res, next) => {
|
164 |
|
165 | void this._handleHttpRequest(req, res).catch(next);
|
166 | });
|
167 |
|
168 | this._expressApp.use(this._unexpectedErrorHandler());
|
169 | }
|
170 | |
171 |
|
172 |
|
173 | _unexpectedErrorHandler() {
|
174 | const handleUnExpectedError = (err, req, res, next) => {
|
175 |
|
176 |
|
177 | this.get(SequenceActions.REJECT, { optional: true })
|
178 | .then(reject => {
|
179 | if (reject) {
|
180 |
|
181 |
|
182 | return reject({ request: req, response: res }, err);
|
183 | }
|
184 |
|
185 | (0, strong_error_handler_1.writeErrorToResponse)(err, req, res);
|
186 | })
|
187 | .catch(unexpectedErr => next(unexpectedErr));
|
188 | };
|
189 | return handleUnExpectedError;
|
190 | }
|
191 | |
192 |
|
193 |
|
194 | _applyExpressSettings() {
|
195 | assertExists(this._expressApp, 'this._expressApp');
|
196 | const settings = this.config.expressSettings;
|
197 | for (const key in settings) {
|
198 | this._expressApp.set(key, settings[key]);
|
199 | }
|
200 | if (this.config.router && typeof this.config.router.strict === 'boolean') {
|
201 | this._expressApp.set('strict routing', this.config.router.strict);
|
202 | }
|
203 | }
|
204 | |
205 |
|
206 |
|
207 |
|
208 | _setupOpenApiSpecEndpoints() {
|
209 | assertExists(this._expressApp, 'this._expressApp');
|
210 | if (this.config.openApiSpec.disabled)
|
211 | return;
|
212 | const router = express_2.default.Router();
|
213 | const mapping = this.config.openApiSpec.endpointMapping;
|
214 |
|
215 | for (const p in mapping) {
|
216 | this.addOpenApiSpecEndpoint(p, mapping[p], router);
|
217 | }
|
218 | const explorerPaths = ['/swagger-ui', '/explorer'];
|
219 | router.get(explorerPaths, (req, res, next) => this._redirectToSwaggerUI(req, res, next));
|
220 | this.expressMiddleware('middleware.apiSpec.defaults', router, {
|
221 | group: sequence_1.RestMiddlewareGroups.API_SPEC,
|
222 | upstreamGroups: sequence_1.RestMiddlewareGroups.CORS,
|
223 | }).apply((0, core_1.extensionFor)(keys_1.RestTags.REST_MIDDLEWARE_CHAIN, keys_1.RestTags.ACTION_MIDDLEWARE_CHAIN));
|
224 | }
|
225 | |
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 | addOpenApiSpecEndpoint(path, form, router) {
|
232 | if (router == null) {
|
233 | const key = `middleware.apiSpec.${path}.${form}`;
|
234 | if (this.contains(key)) {
|
235 | throw new Error(`The path ${path} is already configured for OpenApi hosting`);
|
236 | }
|
237 | const newRouter = express_2.default.Router();
|
238 | newRouter.get(path, (req, res) => this._serveOpenApiSpec(req, res, form));
|
239 | this.expressMiddleware(() => newRouter, {}, {
|
240 | injectConfiguration: false,
|
241 | key: `middleware.apiSpec.${path}.${form}`,
|
242 | group: 'apiSpec',
|
243 | });
|
244 | }
|
245 | else {
|
246 | router.get(path, (req, res) => this._serveOpenApiSpec(req, res, form));
|
247 | }
|
248 | }
|
249 | _handleHttpRequest(request, response) {
|
250 | return this.httpHandler.handleRequest(request, response);
|
251 | }
|
252 | _setupHandlerIfNeeded() {
|
253 | if (this._httpHandler)
|
254 | return;
|
255 |
|
256 |
|
257 | const routesObserver = {
|
258 | filter: binding => (0, core_1.filterByKey)(keys_1.RestBindings.API_SPEC.key)(binding) ||
|
259 | ((0, core_1.filterByKey)(/^(controllers|routes)\..+/)(binding) &&
|
260 |
|
261 | !(0, core_1.filterByTag)(keys_1.RestTags.CONTROLLER_ROUTE)(binding)),
|
262 | observe: () => {
|
263 |
|
264 |
|
265 | this._createHttpHandler();
|
266 | },
|
267 | };
|
268 | this._routesEventSubscription = this.subscribe(routesObserver);
|
269 | this._createHttpHandler();
|
270 | }
|
271 | |
272 |
|
273 |
|
274 | _createHttpHandler() {
|
275 | |
276 |
|
277 |
|
278 | const router = this.getSync(keys_1.RestBindings.ROUTER, { optional: true });
|
279 | const routingTable = new router_1.RoutingTable(router, this._externalRoutes);
|
280 | this._httpHandler = new http_handler_1.HttpHandler(this, this.config, routingTable);
|
281 |
|
282 | for (const b of this.findByTag(keys_1.RestTags.CONTROLLER_ROUTE)) {
|
283 | this.unbind(b.key);
|
284 | }
|
285 | for (const b of this.find(`${core_1.CoreBindings.CONTROLLERS}.*`)) {
|
286 | const controllerName = b.key.replace(/^controllers\./, '');
|
287 | const ctor = b.valueConstructor;
|
288 | if (!ctor) {
|
289 | throw new Error(`The controller ${controllerName} was not bound via .toClass()`);
|
290 | }
|
291 | const apiSpec = (0, openapi_v3_1.getControllerSpec)(ctor);
|
292 | if (!apiSpec) {
|
293 |
|
294 | debug('Skipping controller %s - no API spec provided', controllerName);
|
295 | continue;
|
296 | }
|
297 | debug('Registering controller %s', controllerName);
|
298 | if (apiSpec.components) {
|
299 | this._httpHandler.registerApiComponents(apiSpec.components);
|
300 | }
|
301 | const controllerFactory = (0, router_1.createControllerFactoryForBinding)(b.key);
|
302 | const routes = (0, router_1.createRoutesForController)(apiSpec, ctor, controllerFactory);
|
303 | for (const route of routes) {
|
304 | const binding = this.bindRoute(route);
|
305 | binding
|
306 | .tag(keys_1.RestTags.CONTROLLER_ROUTE)
|
307 | .tag({ [keys_1.RestTags.CONTROLLER_BINDING]: b.key });
|
308 | }
|
309 | }
|
310 | for (const b of this.findByTag(keys_1.RestTags.REST_ROUTE)) {
|
311 |
|
312 | const route = this.getSync(b.key);
|
313 | this._httpHandler.registerRoute(route);
|
314 | }
|
315 |
|
316 | const spec = this.getSync(keys_1.RestBindings.API_SPEC);
|
317 | if (spec.components) {
|
318 | this._httpHandler.registerApiComponents(spec.components);
|
319 | }
|
320 | for (const path in spec.paths) {
|
321 | for (const verb in spec.paths[path]) {
|
322 | const routeSpec = spec.paths[path][verb];
|
323 | this._setupOperation(verb, path, routeSpec);
|
324 | }
|
325 | }
|
326 | }
|
327 | _setupOperation(verb, path, spec) {
|
328 | const handler = spec['x-operation'];
|
329 | if (typeof handler === 'function') {
|
330 |
|
331 |
|
332 |
|
333 | spec = Object.assign({}, spec);
|
334 | delete spec['x-operation'];
|
335 | const route = new router_1.Route(verb, path, spec, handler);
|
336 | this._httpHandler.registerRoute(route);
|
337 | return;
|
338 | }
|
339 | const controllerName = spec['x-controller-name'];
|
340 | if (typeof controllerName === 'string') {
|
341 | const b = this.getBinding(`controllers.${controllerName}`, {
|
342 | optional: true,
|
343 | });
|
344 | if (!b) {
|
345 | throw new Error(`Unknown controller ${controllerName} used by "${verb} ${path}"`);
|
346 | }
|
347 | const ctor = b.valueConstructor;
|
348 | if (!ctor) {
|
349 | throw new Error(`The controller ${controllerName} was not bound via .toClass()`);
|
350 | }
|
351 | const controllerFactory = (0, router_1.createControllerFactoryForBinding)(b.key);
|
352 | const route = new router_1.ControllerRoute(verb, path, spec, ctor, controllerFactory);
|
353 | this._httpHandler.registerRoute(route);
|
354 | return;
|
355 | }
|
356 | throw new Error(`There is no handler configured for operation "${verb} ${path}`);
|
357 | }
|
358 | async _serveOpenApiSpec(request, response, specForm) {
|
359 | const requestContext = new request_context_1.RequestContext(request, response, this, this.config);
|
360 | specForm = specForm !== null && specForm !== void 0 ? specForm : { version: '3.0.0', format: 'json' };
|
361 | const specObj = await this.getApiSpec(requestContext);
|
362 | if (specForm.format === 'json') {
|
363 | const spec = JSON.stringify(specObj, null, 2);
|
364 | response.setHeader('content-type', 'application/json; charset=utf-8');
|
365 | response.end(spec, 'utf-8');
|
366 | }
|
367 | else {
|
368 | const yaml = (0, js_yaml_1.dump)(specObj, {});
|
369 | response.setHeader('content-type', 'text/yaml; charset=utf-8');
|
370 | response.end(yaml, 'utf-8');
|
371 | }
|
372 | }
|
373 | async _redirectToSwaggerUI(request, response, next) {
|
374 | const config = this.config.apiExplorer;
|
375 | if (config.disabled) {
|
376 | debug('Redirect to swagger-ui was disabled by configuration.');
|
377 | next();
|
378 | return;
|
379 | }
|
380 | debug('Redirecting to swagger-ui from %j.', request.originalUrl);
|
381 | const requestContext = new request_context_1.RequestContext(request, response, this, this.config);
|
382 | const protocol = requestContext.requestedProtocol;
|
383 | const baseUrl = protocol === 'http' ? config.httpUrl : config.url;
|
384 | const openApiUrl = `${requestContext.requestedBaseUrl}/openapi.json`;
|
385 | const fullUrl = `${baseUrl}?url=${openApiUrl}`;
|
386 | response.redirect(302, fullUrl);
|
387 | }
|
388 | |
389 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 | controller(controllerCtor) {
|
406 | return this.bind('controllers.' + controllerCtor.name).toClass(controllerCtor);
|
407 | }
|
408 | route(routeOrVerb, path, spec, controllerCtorOrHandler, controllerFactory, methodName) {
|
409 | if (typeof routeOrVerb === 'object') {
|
410 | const r = routeOrVerb;
|
411 |
|
412 | return this.bindRoute(r);
|
413 | }
|
414 | if (!path) {
|
415 | throw new assert_1.AssertionError({
|
416 | message: 'path is required for a controller-based route',
|
417 | });
|
418 | }
|
419 | if (!spec) {
|
420 | throw new assert_1.AssertionError({
|
421 | message: 'spec is required for a controller-based route',
|
422 | });
|
423 | }
|
424 | if (arguments.length === 4) {
|
425 | if (!controllerCtorOrHandler) {
|
426 | throw new assert_1.AssertionError({
|
427 | message: 'handler function is required for a handler-based route',
|
428 | });
|
429 | }
|
430 | return this.route(new router_1.Route(routeOrVerb, path, spec, controllerCtorOrHandler));
|
431 | }
|
432 | if (!controllerCtorOrHandler) {
|
433 | throw new assert_1.AssertionError({
|
434 | message: 'controller is required for a controller-based route',
|
435 | });
|
436 | }
|
437 | if (!methodName) {
|
438 | throw new assert_1.AssertionError({
|
439 | message: 'methodName is required for a controller-based route',
|
440 | });
|
441 | }
|
442 | return this.route(new router_1.ControllerRoute(routeOrVerb, path, spec, controllerCtorOrHandler, controllerFactory, methodName));
|
443 | }
|
444 | bindRoute(r) {
|
445 | const namespace = keys_1.RestBindings.ROUTES;
|
446 | const encodedPath = encodeURIComponent(r.path).replace(/\./g, '%2E');
|
447 | return this.bind(`${namespace}.${r.verb} ${encodedPath}`)
|
448 | .to(r)
|
449 | .tag(keys_1.RestTags.REST_ROUTE)
|
450 | .tag({ [keys_1.RestTags.ROUTE_VERB]: r.verb, [keys_1.RestTags.ROUTE_PATH]: r.path });
|
451 | }
|
452 | |
453 |
|
454 |
|
455 |
|
456 |
|
457 |
|
458 |
|
459 |
|
460 |
|
461 |
|
462 |
|
463 |
|
464 |
|
465 |
|
466 |
|
467 | redirect(fromPath, toPathOrUrl, statusCode) {
|
468 | return this.route(new router_1.RedirectRoute(fromPath, this._basePath + toPathOrUrl, statusCode));
|
469 | }
|
470 | |
471 |
|
472 |
|
473 |
|
474 |
|
475 |
|
476 |
|
477 |
|
478 | static(path, rootDir, options) {
|
479 | this._externalRoutes.registerAssets(path, rootDir, options);
|
480 | }
|
481 | |
482 |
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 | api(spec) {
|
494 | return this.bind(keys_1.RestBindings.API_SPEC).to(spec);
|
495 | }
|
496 | |
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 |
|
512 |
|
513 |
|
514 |
|
515 |
|
516 |
|
517 | async getApiSpec(requestContext) {
|
518 | let spec = await this.get(keys_1.RestBindings.API_SPEC);
|
519 | spec = (0, lodash_1.cloneDeep)(spec);
|
520 | const components = this.httpHandler.getApiComponents();
|
521 |
|
522 |
|
523 | const paths = (0, lodash_1.cloneDeep)(this.httpHandler.describeApiPaths());
|
524 | spec.paths = { ...paths, ...spec.paths };
|
525 | if (components) {
|
526 | const defs = (0, lodash_1.cloneDeep)(components);
|
527 | spec.components = { ...spec.components, ...defs };
|
528 | }
|
529 | (0, router_spec_1.assignRouterSpec)(spec, this._externalRoutes.routerSpec);
|
530 | if (requestContext) {
|
531 | spec = this.updateSpecFromRequest(spec, requestContext);
|
532 | }
|
533 |
|
534 | this.OASEnhancer.spec = spec;
|
535 | spec = await this.OASEnhancer.applyAllEnhancers();
|
536 | return spec;
|
537 | }
|
538 | |
539 |
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 | updateSpecFromRequest(spec, requestContext) {
|
548 | if (this.config.openApiSpec.setServersFromRequest) {
|
549 | spec = Object.assign({}, spec);
|
550 | spec.servers = [{ url: requestContext.requestedBaseUrl }];
|
551 | }
|
552 | const basePath = requestContext.basePath;
|
553 | if (spec.servers && basePath) {
|
554 | for (const s of spec.servers) {
|
555 |
|
556 | if (s.url === '/') {
|
557 | s.url = basePath;
|
558 | }
|
559 | }
|
560 | }
|
561 | return spec;
|
562 | }
|
563 | |
564 |
|
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 |
|
579 |
|
580 |
|
581 | sequence(sequenceClass) {
|
582 | const sequenceBinding = (0, core_1.createBindingFromClass)(sequenceClass, {
|
583 | key: keys_1.RestBindings.SEQUENCE,
|
584 | });
|
585 | this.add(sequenceBinding);
|
586 | return sequenceBinding;
|
587 | }
|
588 | |
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 |
|
597 |
|
598 |
|
599 |
|
600 | handler(handlerFn) {
|
601 | class SequenceFromFunction extends sequence_1.DefaultSequence {
|
602 | async handle(context) {
|
603 | return handlerFn(context, this);
|
604 | }
|
605 | }
|
606 | this.sequence(SequenceFromFunction);
|
607 | }
|
608 | |
609 |
|
610 |
|
611 |
|
612 |
|
613 | bodyParser(bodyParserClass, address) {
|
614 | const binding = createBodyParserBinding(bodyParserClass, address);
|
615 | this.add(binding);
|
616 | return binding;
|
617 | }
|
618 | |
619 |
|
620 |
|
621 |
|
622 | basePath(path = '') {
|
623 | if (this._requestHandler != null) {
|
624 | throw new Error('Base path cannot be set as the request handler has been created');
|
625 | }
|
626 |
|
627 | path = path.replace(/(^\/)|(\/$)/, '');
|
628 | if (path)
|
629 | path = '/' + path;
|
630 | this._basePath = path;
|
631 | this.config.basePath = path;
|
632 | }
|
633 | |
634 |
|
635 |
|
636 | async start() {
|
637 |
|
638 | this._setupRequestHandlerIfNeeded();
|
639 |
|
640 |
|
641 | this._setupHandlerIfNeeded();
|
642 | const port = await this.get(keys_1.RestBindings.PORT);
|
643 | const host = await this.get(keys_1.RestBindings.HOST);
|
644 | const path = await this.get(keys_1.RestBindings.PATH);
|
645 | const protocol = await this.get(keys_1.RestBindings.PROTOCOL);
|
646 | const httpsOptions = await this.get(keys_1.RestBindings.HTTPS_OPTIONS);
|
647 | if (this.config.listenOnStart === false) {
|
648 | debug('RestServer is not listening as listenOnStart flag is set to false.');
|
649 | return;
|
650 | }
|
651 | const serverOptions = { ...httpsOptions, port, host, protocol, path };
|
652 | this._httpServer = new http_server_1.HttpServer(this.requestHandler, serverOptions);
|
653 | await this._httpServer.start();
|
654 | this.bind(keys_1.RestBindings.PORT).to(this._httpServer.port);
|
655 | this.bind(keys_1.RestBindings.HOST).to(this._httpServer.host);
|
656 | this.bind(keys_1.RestBindings.URL).to(this._httpServer.url);
|
657 | debug('RestServer listening at %s', this._httpServer.url);
|
658 | }
|
659 | |
660 |
|
661 |
|
662 | async stop() {
|
663 |
|
664 | if (!this._httpServer)
|
665 | return;
|
666 | await this._httpServer.stop();
|
667 | this._httpServer = undefined;
|
668 | }
|
669 | |
670 |
|
671 |
|
672 |
|
673 |
|
674 |
|
675 |
|
676 |
|
677 |
|
678 |
|
679 |
|
680 | mountExpressRouter(basePath, router, spec) {
|
681 | this._externalRoutes.mountRouter(basePath, router, spec);
|
682 | }
|
683 | |
684 |
|
685 |
|
686 |
|
687 |
|
688 |
|
689 |
|
690 |
|
691 |
|
692 |
|
693 | async exportOpenApiSpec(outFile = '', log = console.log) {
|
694 | const spec = await this.getApiSpec();
|
695 | if (outFile === '-' || outFile === '') {
|
696 | const json = JSON.stringify(spec, null, 2);
|
697 | log('%s', json);
|
698 | return;
|
699 | }
|
700 | const fileName = outFile.toLowerCase();
|
701 | if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) {
|
702 | const yaml = (0, js_yaml_1.dump)(spec);
|
703 | fs_1.default.writeFileSync(outFile, yaml, 'utf-8');
|
704 | }
|
705 | else {
|
706 | const json = JSON.stringify(spec, null, 2);
|
707 | fs_1.default.writeFileSync(outFile, json, 'utf-8');
|
708 | }
|
709 | log('The OpenAPI spec has been saved to %s.', outFile);
|
710 | }
|
711 | };
|
712 | exports.RestServer = RestServer;
|
713 | exports.RestServer = RestServer = tslib_1.__decorate([
|
714 | tslib_1.__param(0, (0, core_1.inject)(core_1.CoreBindings.APPLICATION_INSTANCE)),
|
715 | tslib_1.__param(1, (0, core_1.inject)(keys_1.RestBindings.CONFIG, { optional: true })),
|
716 | tslib_1.__metadata("design:paramtypes", [core_1.Application, Object])
|
717 | ], RestServer);
|
718 |
|
719 |
|
720 |
|
721 |
|
722 |
|
723 |
|
724 | function assertExists(val, name) {
|
725 | (0, assert_1.default)(val != null, `The value of ${name} cannot be null or undefined`);
|
726 | }
|
727 |
|
728 |
|
729 |
|
730 |
|
731 |
|
732 | function createBodyParserBinding(parserClass, key) {
|
733 | const address = key !== null && key !== void 0 ? key : `${keys_1.RestBindings.REQUEST_BODY_PARSER}.${parserClass.name}`;
|
734 | return core_1.Binding.bind(address)
|
735 | .toClass(parserClass)
|
736 | .inScope(core_1.BindingScope.TRANSIENT)
|
737 | .tag(body_parsers_1.REQUEST_BODY_PARSER_TAG);
|
738 | }
|
739 | exports.createBodyParserBinding = createBodyParserBinding;
|
740 | const OPENAPI_SPEC_MAPPING = {
|
741 | '/openapi.json': { version: '3.0.0', format: 'json' },
|
742 | '/openapi.yaml': { version: '3.0.0', format: 'yaml' },
|
743 | };
|
744 | const DEFAULT_CONFIG = {
|
745 | port: 3000,
|
746 | openApiSpec: {},
|
747 | apiExplorer: {},
|
748 | cors: {
|
749 | origin: '*',
|
750 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
751 | preflightContinue: false,
|
752 | optionsSuccessStatus: 204,
|
753 | maxAge: 86400,
|
754 | credentials: true,
|
755 | },
|
756 | expressSettings: {},
|
757 | router: {},
|
758 | listenOnStart: true,
|
759 | };
|
760 | function resolveRestServerConfig(config) {
|
761 | const result = Object.assign((0, lodash_1.cloneDeep)(DEFAULT_CONFIG), config);
|
762 |
|
763 | if (result.port == null) {
|
764 | result.port = 3000;
|
765 | }
|
766 | if (result.host == null) {
|
767 |
|
768 | result.host = undefined;
|
769 | }
|
770 | if (!result.openApiSpec.endpointMapping) {
|
771 |
|
772 |
|
773 | result.openApiSpec.endpointMapping = (0, lodash_1.cloneDeep)(OPENAPI_SPEC_MAPPING);
|
774 | }
|
775 | result.apiExplorer = normalizeApiExplorerConfig(config.apiExplorer);
|
776 | if (result.openApiSpec.disabled) {
|
777 |
|
778 | result.apiExplorer.disabled = true;
|
779 | }
|
780 | return result;
|
781 | }
|
782 | function normalizeApiExplorerConfig(input) {
|
783 | var _a, _b, _c;
|
784 | const config = input !== null && input !== void 0 ? input : {};
|
785 | const url = (_a = config.url) !== null && _a !== void 0 ? _a : 'https://explorer.loopback.io';
|
786 | config.httpUrl =
|
787 | (_c = (_b = config.httpUrl) !== null && _b !== void 0 ? _b : config.url) !== null && _c !== void 0 ? _c : 'http://explorer.loopback.io';
|
788 | config.url = url;
|
789 | return config;
|
790 | }
|
791 |
|
\ | No newline at end of file |