1 | ;
|
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
|
6 | var MiddlewareSequence_1;
|
7 | Object.defineProperty(exports, "__esModule", { value: true });
|
8 | exports.MiddlewareSequence = exports.RestMiddlewareGroups = exports.DefaultSequence = void 0;
|
9 | const tslib_1 = require("tslib");
|
10 | const core_1 = require("@loopback/core");
|
11 | const express_1 = require("@loopback/express");
|
12 | const debug_1 = tslib_1.__importDefault(require("debug"));
|
13 | const keys_1 = require("./keys");
|
14 | const debug = (0, debug_1.default)('loopback:rest:sequence');
|
15 | const 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 | */
|
34 | let 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 | };
|
99 | exports.DefaultSequence = DefaultSequence;
|
100 | tslib_1.__decorate([
|
101 | (0, core_1.inject)(SequenceActions.INVOKE_MIDDLEWARE, { optional: true }),
|
102 | tslib_1.__metadata("design:type", Function)
|
103 | ], DefaultSequence.prototype, "invokeMiddleware", void 0);
|
104 | exports.DefaultSequence = DefaultSequence = tslib_1.__decorate([
|
105 | tslib_1.__param(0, (0, core_1.inject)(SequenceActions.FIND_ROUTE)),
|
106 | tslib_1.__param(1, (0, core_1.inject)(SequenceActions.PARSE_PARAMS)),
|
107 | tslib_1.__param(2, (0, core_1.inject)(SequenceActions.INVOKE_METHOD)),
|
108 | tslib_1.__param(3, (0, core_1.inject)(SequenceActions.SEND)),
|
109 | tslib_1.__param(4, (0, core_1.inject)(SequenceActions.REJECT)),
|
110 | tslib_1.__metadata("design:paramtypes", [Function, Function, Function, Function, Function])
|
111 | ], DefaultSequence);
|
112 | /**
|
113 | * Built-in middleware groups for the REST sequence
|
114 | */
|
115 | var 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 = RestMiddlewareGroups = {}));
|
152 | /**
|
153 | * A sequence implementation using middleware chains
|
154 | */
|
155 | let 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 | };
|
204 | exports.MiddlewareSequence = MiddlewareSequence;
|
205 | MiddlewareSequence.defaultOptions = {
|
206 | chain: keys_1.RestTags.REST_MIDDLEWARE_CHAIN,
|
207 | orderedGroups: [
|
208 | // Please note that middleware is cascading. The `sendResponse` is
|
209 | // added first to invoke downstream middleware to get the result or
|
210 | // catch errors so that it can produce the http response.
|
211 | RestMiddlewareGroups.SEND_RESPONSE,
|
212 | RestMiddlewareGroups.CORS,
|
213 | RestMiddlewareGroups.API_SPEC,
|
214 | RestMiddlewareGroups.MIDDLEWARE,
|
215 | RestMiddlewareGroups.FIND_ROUTE,
|
216 | // authentication depends on the route
|
217 | RestMiddlewareGroups.AUTHENTICATION,
|
218 | RestMiddlewareGroups.PARSE_PARAMS,
|
219 | RestMiddlewareGroups.INVOKE_METHOD,
|
220 | ],
|
221 | /**
|
222 | * Reports an error if there are middleware groups are unreachable as they
|
223 | * are ordered after the `invokeMethod` group.
|
224 | */
|
225 | validate: groups => {
|
226 | const index = groups.indexOf(RestMiddlewareGroups.INVOKE_METHOD);
|
227 | if (index !== -1) {
|
228 | const unreachableGroups = groups.slice(index + 1);
|
229 | if (unreachableGroups.length > 0) {
|
230 | throw new Error(`Middleware groups "${unreachableGroups.join(',')}" are not invoked as they are ordered after "${RestMiddlewareGroups.INVOKE_METHOD}"`);
|
231 | }
|
232 | }
|
233 | },
|
234 | };
|
235 | exports.MiddlewareSequence = MiddlewareSequence = MiddlewareSequence_1 = tslib_1.__decorate([
|
236 | (0, core_1.injectable)({ scope: core_1.BindingScope.SINGLETON }),
|
237 | tslib_1.__param(0, core_1.inject.context()),
|
238 | tslib_1.__param(1, (0, core_1.inject)(keys_1.RestBindings.INVOKE_MIDDLEWARE_SERVICE)),
|
239 | tslib_1.__param(2, (0, core_1.config)()),
|
240 | tslib_1.__metadata("design:paramtypes", [core_1.Context, Function, Object])
|
241 | ], MiddlewareSequence);
|
242 | //# sourceMappingURL=sequence.js.map |
\ | No newline at end of file |