UNPKG

10.3 kBJavaScriptView Raw
1"use strict";
2// Copyright IBM Corp. and LoopBack contributors 2017,2020. All Rights Reserved.
3// Node module: @loopback/rest
4// This file is licensed under the MIT License.
5// License text available at https://opensource.org/licenses/MIT
6var MiddlewareSequence_1;
7Object.defineProperty(exports, "__esModule", { value: true });
8exports.MiddlewareSequence = exports.RestMiddlewareGroups = exports.DefaultSequence = void 0;
9const tslib_1 = require("tslib");
10const core_1 = require("@loopback/core");
11const express_1 = require("@loopback/express");
12const debug_1 = tslib_1.__importDefault(require("debug"));
13const keys_1 = require("./keys");
14const debug = (0, debug_1.default)('loopback:rest:sequence');
15const SequenceActions = keys_1.RestBindings.SequenceActions;
16/**
17 * The default implementation of SequenceHandler.
18 *
19 * @remarks
20 * This class implements default Sequence for the LoopBack framework.
21 * Default sequence is used if user hasn't defined their own Sequence
22 * for their application.
23 *
24 * Sequence constructor() and run() methods are invoked from [[http-handler]]
25 * when the API request comes in. User defines APIs in their Application
26 * Controller class.
27 *
28 * @example
29 * User can bind their own Sequence to app as shown below
30 * ```ts
31 * app.bind(CoreBindings.SEQUENCE).toClass(MySequence);
32 * ```
33 */
34let DefaultSequence = class DefaultSequence {
35 /**
36 * Constructor: Injects findRoute, invokeMethod & logError
37 * methods as promises.
38 *
39 * @param findRoute - Finds the appropriate controller method,
40 * spec and args for invocation (injected via SequenceActions.FIND_ROUTE).
41 * @param parseParams - The parameter parsing function (injected
42 * via SequenceActions.PARSE_PARAMS).
43 * @param invoke - Invokes the method specified by the route
44 * (injected via SequenceActions.INVOKE_METHOD).
45 * @param send - The action to merge the invoke result with the response
46 * (injected via SequenceActions.SEND)
47 * @param reject - The action to take if the invoke returns a rejected
48 * promise result (injected via SequenceActions.REJECT).
49 */
50 constructor(findRoute, parseParams, invoke, send, reject) {
51 this.findRoute = findRoute;
52 this.parseParams = parseParams;
53 this.invoke = invoke;
54 this.send = send;
55 this.reject = reject;
56 /**
57 * Optional invoker for registered middleware in a chain.
58 * To be injected via SequenceActions.INVOKE_MIDDLEWARE.
59 */
60 this.invokeMiddleware = () => false;
61 }
62 /**
63 * Runs the default sequence. Given a handler context (request and response),
64 * running the sequence will produce a response or an error.
65 *
66 * Default sequence executes these steps
67 * - Executes middleware for CORS, OpenAPI spec endpoints
68 * - Finds the appropriate controller method, swagger spec
69 * and args for invocation
70 * - Parses HTTP request to get API argument list
71 * - Invokes the API which is defined in the Application Controller
72 * - Writes the result from API into the HTTP response
73 * - Error is caught and logged using 'logError' if any of the above steps
74 * in the sequence fails with an error.
75 *
76 * @param context - The request context: HTTP request and response objects,
77 * per-request IoC container and more.
78 */
79 async handle(context) {
80 try {
81 const { request, response } = context;
82 // Invoke registered Express middleware
83 const finished = await this.invokeMiddleware(context);
84 if (finished) {
85 // The response been produced by the middleware chain
86 return;
87 }
88 const route = this.findRoute(request);
89 const args = await this.parseParams(request, route);
90 const result = await this.invoke(route, args);
91 debug('%s result -', route.describe(), result);
92 this.send(response, result);
93 }
94 catch (error) {
95 this.reject(context, error);
96 }
97 }
98};
99tslib_1.__decorate([
100 (0, core_1.inject)(SequenceActions.INVOKE_MIDDLEWARE, { optional: true }),
101 tslib_1.__metadata("design:type", Function)
102], DefaultSequence.prototype, "invokeMiddleware", void 0);
103DefaultSequence = tslib_1.__decorate([
104 tslib_1.__param(0, (0, core_1.inject)(SequenceActions.FIND_ROUTE)),
105 tslib_1.__param(1, (0, core_1.inject)(SequenceActions.PARSE_PARAMS)),
106 tslib_1.__param(2, (0, core_1.inject)(SequenceActions.INVOKE_METHOD)),
107 tslib_1.__param(3, (0, core_1.inject)(SequenceActions.SEND)),
108 tslib_1.__param(4, (0, core_1.inject)(SequenceActions.REJECT)),
109 tslib_1.__metadata("design:paramtypes", [Function, Function, Function, Function, Function])
110], DefaultSequence);
111exports.DefaultSequence = DefaultSequence;
112/**
113 * Built-in middleware groups for the REST sequence
114 */
115var RestMiddlewareGroups;
116(function (RestMiddlewareGroups) {
117 /**
118 * Invoke downstream middleware to get the result or catch errors so that it
119 * can produce the http response
120 */
121 RestMiddlewareGroups.SEND_RESPONSE = 'sendResponse';
122 /**
123 * Enforce CORS
124 */
125 RestMiddlewareGroups.CORS = express_1.MiddlewareGroups.CORS;
126 /**
127 * Server OpenAPI specs
128 */
129 RestMiddlewareGroups.API_SPEC = express_1.MiddlewareGroups.API_SPEC;
130 /**
131 * Default middleware group
132 */
133 RestMiddlewareGroups.MIDDLEWARE = express_1.MiddlewareGroups.MIDDLEWARE;
134 RestMiddlewareGroups.DEFAULT = RestMiddlewareGroups.MIDDLEWARE;
135 /**
136 * Find the route that can serve the request
137 */
138 RestMiddlewareGroups.FIND_ROUTE = 'findRoute';
139 /**
140 * Perform authentication
141 */
142 RestMiddlewareGroups.AUTHENTICATION = 'authentication';
143 /**
144 * Parse the http request to extract parameter values for the operation
145 */
146 RestMiddlewareGroups.PARSE_PARAMS = 'parseParams';
147 /**
148 * Invoke the target controller method or handler function
149 */
150 RestMiddlewareGroups.INVOKE_METHOD = 'invokeMethod';
151})(RestMiddlewareGroups = exports.RestMiddlewareGroups || (exports.RestMiddlewareGroups = {}));
152/**
153 * A sequence implementation using middleware chains
154 */
155let MiddlewareSequence = MiddlewareSequence_1 = class MiddlewareSequence {
156 /**
157 * Constructor: Injects `InvokeMiddleware` and `InvokeMiddlewareOptions`
158 *
159 * @param invokeMiddleware - invoker for registered middleware in a chain.
160 * To be injected via RestBindings.INVOKE_MIDDLEWARE_SERVICE.
161 */
162 constructor(context, invokeMiddleware, options = MiddlewareSequence_1.defaultOptions) {
163 this.invokeMiddleware = invokeMiddleware;
164 this.options = options;
165 this.middlewareView = new express_1.MiddlewareView(context, options);
166 debug('Discovered middleware', this.middlewareView.middlewareBindingKeys);
167 }
168 /**
169 * Runs the default sequence. Given a handler context (request and response),
170 * running the sequence will produce a response or an error.
171 *
172 * Default sequence executes these groups of middleware:
173 *
174 * - `cors`: Enforces `CORS`
175 * - `openApiSpec`: Serves OpenAPI specs
176 * - `findRoute`: Finds the appropriate controller method, swagger spec and
177 * args for invocation
178 * - `parseParams`: Parses HTTP request to get API argument list
179 * - `invokeMethod`: Invokes the API which is defined in the Application
180 * controller method
181 *
182 * In front of the groups above, we have a special middleware called
183 * `sendResponse`, which first invokes downstream middleware to get a result
184 * and handles the result or error respectively.
185 *
186 * - Writes the result from API into the HTTP response (if the HTTP response
187 * has not been produced yet by the middleware chain.
188 * - Catches error logs it using 'logError' if any of the above steps
189 * in the sequence fails with an error.
190 *
191 * @param context - The request context: HTTP request and response objects,
192 * per-request IoC container and more.
193 */
194 async handle(context) {
195 debug('Invoking middleware chain %s with groups %s', this.options.chain, this.options.orderedGroups);
196 const options = {
197 middlewareList: this.middlewareView.middlewareBindingKeys,
198 validate: MiddlewareSequence_1.defaultOptions.validate,
199 ...this.options,
200 };
201 await this.invokeMiddleware(context, options);
202 }
203};
204MiddlewareSequence.defaultOptions = {
205 chain: keys_1.RestTags.REST_MIDDLEWARE_CHAIN,
206 orderedGroups: [
207 // Please note that middleware is cascading. The `sendResponse` is
208 // added first to invoke downstream middleware to get the result or
209 // catch errors so that it can produce the http response.
210 RestMiddlewareGroups.SEND_RESPONSE,
211 RestMiddlewareGroups.CORS,
212 RestMiddlewareGroups.API_SPEC,
213 RestMiddlewareGroups.MIDDLEWARE,
214 RestMiddlewareGroups.FIND_ROUTE,
215 // authentication depends on the route
216 RestMiddlewareGroups.AUTHENTICATION,
217 RestMiddlewareGroups.PARSE_PARAMS,
218 RestMiddlewareGroups.INVOKE_METHOD,
219 ],
220 /**
221 * Reports an error if there are middleware groups are unreachable as they
222 * are ordered after the `invokeMethod` group.
223 */
224 validate: groups => {
225 const index = groups.indexOf(RestMiddlewareGroups.INVOKE_METHOD);
226 if (index !== -1) {
227 const unreachableGroups = groups.slice(index + 1);
228 if (unreachableGroups.length > 0) {
229 throw new Error(`Middleware groups "${unreachableGroups.join(',')}" are not invoked as they are ordered after "${RestMiddlewareGroups.INVOKE_METHOD}"`);
230 }
231 }
232 },
233};
234MiddlewareSequence = MiddlewareSequence_1 = tslib_1.__decorate([
235 (0, core_1.injectable)({ scope: core_1.BindingScope.SINGLETON }),
236 tslib_1.__param(0, core_1.inject.context()),
237 tslib_1.__param(1, (0, core_1.inject)(keys_1.RestBindings.INVOKE_MIDDLEWARE_SERVICE)),
238 tslib_1.__param(2, (0, core_1.config)()),
239 tslib_1.__metadata("design:paramtypes", [core_1.Context, Function, Object])
240], MiddlewareSequence);
241exports.MiddlewareSequence = MiddlewareSequence;
242//# sourceMappingURL=sequence.js.map
\No newline at end of file