diff --git a/packages/rest/src/rest.application.ts b/packages/rest/src/rest.application.ts index 2ef2baa71e9d..a1bb08e6bd00 100644 --- a/packages/rest/src/rest.application.ts +++ b/packages/rest/src/rest.application.ts @@ -3,22 +3,21 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -import {Application, ApplicationConfig, Server} from '@loopback/core'; -import {RestComponent} from './rest.component'; -import {SequenceHandler, SequenceFunction} from './sequence'; import {Binding, Constructor} from '@loopback/context'; +import {Application, ApplicationConfig, Server} from '@loopback/core'; +import {OpenApiSpec, OperationObject} from '@loopback/openapi-v3-types'; +import {PathParams} from 'express-serve-static-core'; +import {ServeStaticOptions} from 'serve-static'; import {format} from 'util'; import {RestBindings} from './keys'; -import {RestServer, HttpRequestListener, HttpServerLike} from './rest.server'; +import {RestComponent} from './rest.component'; +import {HttpRequestListener, HttpServerLike, RestServer} from './rest.server'; import { - RouteEntry, ControllerClass, ControllerFactory, - Route, + RouteEntry, } from './router/routing-table'; -import {OperationObject, OpenApiSpec} from '@loopback/openapi-v3-types'; -import {ServeStaticOptions} from 'serve-static'; -import {PathParams} from 'express-serve-static-core'; +import {SequenceFunction, SequenceHandler} from './sequence'; export const ERR_NO_MULTI_SERVER = format( 'RestApplication does not support multiple servers!', @@ -127,6 +126,29 @@ export class RestApplication extends Application implements HttpServerLike { methodName: string, ): Binding; + /** + * Register a new route invoking a handler function. + * + * ```ts + * function greet(name: string) { + * return `hello ${name}`; + * } + * app.route('get', '/', operationSpec, greet); + * ``` + * + * @param verb HTTP verb of the endpoint + * @param path URL path of the endpoint + * @param spec The OpenAPI spec describing the endpoint (operation) + * @param handler The function to invoke with the request parameters + * described in the spec. + */ + route( + verb: string, + path: string, + spec: OperationObject, + handler: Function, + ): Binding; + /** * Register a new route. * @@ -172,7 +194,10 @@ export class RestApplication extends Application implements HttpServerLike { return server.route(routeOrVerb); } else if (arguments.length === 4) { return server.route( - new Route(routeOrVerb, path!, spec!, controllerCtorOrHandler!), + routeOrVerb, + path!, + spec!, + controllerCtorOrHandler as Function, ); } else { return server.route( diff --git a/packages/rest/src/rest.server.ts b/packages/rest/src/rest.server.ts index b4d4ec44b152..12d835f3b950 100644 --- a/packages/rest/src/rest.server.ts +++ b/packages/rest/src/rest.server.ts @@ -520,7 +520,30 @@ export class RestServer extends Context implements Server, HttpServerLike { ): Binding; /** - * Register a new route. + * Register a new route invoking a handler function. + * + * ```ts + * function greet(name: string) { + * return `hello ${name}`; + * } + * app.route('get', '/', operationSpec, greet); + * ``` + * + * @param verb HTTP verb of the endpoint + * @param path URL path of the endpoint + * @param spec The OpenAPI spec describing the endpoint (operation) + * @param handler The function to invoke with the request parameters + * described in the spec. + */ + route( + verb: string, + path: string, + spec: OperationObject, + handler: Function, + ): Binding; + + /** + * Register a new generic route. * * ```ts * function greet(name: string) { @@ -534,12 +557,12 @@ export class RestServer extends Context implements Server, HttpServerLike { */ route(route: RouteEntry): Binding; - route( + route( routeOrVerb: RouteEntry | string, path?: string, spec?: OperationObject, - controllerCtor?: ControllerClass, - controllerFactory?: ControllerFactory, + controllerCtorOrHandler?: ControllerClass | Function, + controllerFactory?: ControllerFactory, methodName?: string, ): Binding { if (typeof routeOrVerb === 'object') { @@ -563,7 +586,18 @@ export class RestServer extends Context implements Server, HttpServerLike { }); } - if (!controllerCtor) { + if (arguments.length === 4) { + if (!controllerCtorOrHandler) { + throw new AssertionError({ + message: 'handler function is required for a handler-based route', + }); + } + return this.route( + new Route(routeOrVerb, path, spec, controllerCtorOrHandler as Function), + ); + } + + if (!controllerCtorOrHandler) { throw new AssertionError({ message: 'controller is required for a controller-based route', }); @@ -580,7 +614,7 @@ export class RestServer extends Context implements Server, HttpServerLike { routeOrVerb, path, spec, - controllerCtor, + controllerCtorOrHandler as ControllerClass, controllerFactory, methodName, ), diff --git a/packages/rest/test/acceptance/routing/routing.acceptance.ts b/packages/rest/test/acceptance/routing/routing.acceptance.ts index ee1af228a096..dc75a3c6bd85 100644 --- a/packages/rest/test/acceptance/routing/routing.acceptance.ts +++ b/packages/rest/test/acceptance/routing/routing.acceptance.ts @@ -6,7 +6,6 @@ import { Request, Response, - Route, RestBindings, RestServer, RestComponent, @@ -408,8 +407,7 @@ describe('Routing', () => { return `hello ${name}`; } - const route = new Route('get', '/greet', routeSpec, greet); - server.route(route); + server.route('get', '/greet', routeSpec, greet); const client = whenIMakeRequestTo(server); await client.get('/greet?name=world').expect(200, 'hello world'); @@ -609,8 +607,7 @@ describe('Routing', () => { return `hello ${name}`; } - const route = new Route('get', '/greet', routeSpec, greet); - app.route(route); + app.route('get', '/greet', routeSpec, greet); await whenIMakeRequestTo(app) .get('/greet?name=world') diff --git a/packages/rest/test/integration/rest.server.integration.ts b/packages/rest/test/integration/rest.server.integration.ts index 3e7601208799..99a6df3c586a 100644 --- a/packages/rest/test/integration/rest.server.integration.ts +++ b/packages/rest/test/integration/rest.server.integration.ts @@ -12,7 +12,7 @@ import { httpsGetAsync, givenHttpServerConfig, } from '@loopback/testlab'; -import {Route, RestBindings, RestServer, RestComponent} from '../..'; +import {RestBindings, RestServer, RestComponent} from '../..'; import {IncomingMessage, ServerResponse} from 'http'; import * as yaml from 'js-yaml'; import * as path from 'path'; @@ -238,7 +238,7 @@ describe('RestServer (integration)', () => { }, }, }; - server.route(new Route('get', '/greet', greetSpec, function greet() {})); + server.route('get', '/greet', greetSpec, function greet() {}); const response = await createClientForHandler(server.requestHandler).get( '/openapi.json', @@ -330,7 +330,7 @@ describe('RestServer (integration)', () => { }, }, }; - server.route(new Route('get', '/greet', greetSpec, function greet() {})); + server.route('get', '/greet', greetSpec, function greet() {}); const response = await createClientForHandler(server.requestHandler).get( '/openapi.yaml', @@ -372,7 +372,7 @@ paths: }, }, }; - server.route(new Route('get', '/greet', greetSpec, function greet() {})); + server.route('get', '/greet', greetSpec, function greet() {}); const response = await createClientForHandler(server.requestHandler).get( '/explorer', diff --git a/packages/rest/test/unit/rest.server/rest.server.open-api-spec.unit.ts b/packages/rest/test/unit/rest.server/rest.server.open-api-spec.unit.ts index 1a6b90065314..0fe7a523eb8a 100644 --- a/packages/rest/test/unit/rest.server/rest.server.open-api-spec.unit.ts +++ b/packages/rest/test/unit/rest.server/rest.server.open-api-spec.unit.ts @@ -7,7 +7,6 @@ import {expect, validateApiSpec} from '@loopback/testlab'; import {Application} from '@loopback/core'; import { RestServer, - Route, RestComponent, createControllerFactoryForClass, } from '../../..'; @@ -51,9 +50,7 @@ describe('RestServer.getApiSpec()', () => { it('binds a route via app.route(route)', () => { function greet() {} - const binding = server.route( - new Route('get', '/greet', {responses: {}}, greet), - ); + const binding = server.route('get', '/greet', {responses: {}}, greet); expect(binding.key).to.eql('routes.get %2Fgreet'); expect(binding.tagNames).containEql('route'); }); @@ -77,7 +74,7 @@ describe('RestServer.getApiSpec()', () => { it('returns routes registered via app.route(route)', () => { function greet() {} - server.route(new Route('get', '/greet', {responses: {}}, greet)); + server.route('get', '/greet', {responses: {}}, greet); const spec = server.getApiSpec(); expect(spec.paths).to.eql({ @@ -195,7 +192,7 @@ describe('RestServer.getApiSpec()', () => { ); function greet() {} - server.route(new Route('get', '/greet', {responses: {}}, greet)); + server.route('get', '/greet', {responses: {}}, greet); const spec = server.getApiSpec(); expect(spec.paths).to.eql({