UNPKG

5.07 kBJavaScriptView Raw
1"use strict";
2// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
3// Node module: @loopback/context
4// This file is licensed under the MIT License.
5// License text available at https://opensource.org/licenses/MIT
6Object.defineProperty(exports, "__esModule", { value: true });
7exports.composeInterceptors = exports.invokeInterceptors = exports.GenericInterceptorChain = void 0;
8const tslib_1 = require("tslib");
9const debug_1 = tslib_1.__importDefault(require("debug"));
10const value_promise_1 = require("./value-promise");
11const debug = (0, debug_1.default)('loopback:context:interceptor-chain');
12/**
13 * Invocation state of an interceptor chain
14 */
15class InterceptorChainState {
16 /**
17 * Create a state for the interceptor chain
18 * @param interceptors - Interceptor functions or binding keys
19 * @param finalHandler - An optional final handler
20 */
21 constructor(interceptors, finalHandler = () => undefined) {
22 this.interceptors = interceptors;
23 this.finalHandler = finalHandler;
24 this._index = 0;
25 }
26 /**
27 * Get the index for the current interceptor
28 */
29 get index() {
30 return this._index;
31 }
32 /**
33 * Check if the chain is done - all interceptors are invoked
34 */
35 done() {
36 return this._index === this.interceptors.length;
37 }
38 /**
39 * Get the next interceptor to be invoked
40 */
41 next() {
42 if (this.done()) {
43 throw new Error('No more interceptor is in the chain');
44 }
45 return this.interceptors[this._index++];
46 }
47}
48/**
49 * A chain of generic interceptors to be invoked for the given context
50 *
51 * @typeParam C - `Context` class or a subclass of `Context`
52 */
53class GenericInterceptorChain {
54 // Implementation
55 constructor(context, interceptors, comparator) {
56 this.context = context;
57 if (typeof interceptors === 'function') {
58 const interceptorsView = context.createView(interceptors, comparator);
59 this.getInterceptors = () => {
60 const bindings = interceptorsView.bindings;
61 if (comparator) {
62 bindings.sort(comparator);
63 }
64 return bindings.map(b => b.key);
65 };
66 }
67 else if (Array.isArray(interceptors)) {
68 this.getInterceptors = () => interceptors;
69 }
70 }
71 /**
72 * Invoke the interceptor chain
73 */
74 invokeInterceptors(finalHandler) {
75 // Create a state for each invocation to provide isolation
76 const state = new InterceptorChainState(this.getInterceptors(), finalHandler);
77 return this.next(state);
78 }
79 /**
80 * Use the interceptor chain as an interceptor
81 */
82 asInterceptor() {
83 return (ctx, next) => {
84 return this.invokeInterceptors(next);
85 };
86 }
87 /**
88 * Invoke downstream interceptors or the target method
89 */
90 next(state) {
91 if (state.done()) {
92 // No more interceptors
93 return state.finalHandler();
94 }
95 // Invoke the next interceptor in the chain
96 return this.invokeNextInterceptor(state);
97 }
98 /**
99 * Invoke downstream interceptors
100 */
101 invokeNextInterceptor(state) {
102 const index = state.index;
103 const interceptor = state.next();
104 const interceptorFn = this.loadInterceptor(interceptor);
105 return (0, value_promise_1.transformValueOrPromise)(interceptorFn, fn => {
106 /* istanbul ignore if */
107 if (debug.enabled) {
108 debug('Invoking interceptor %d (%s) on %s', index, fn.name);
109 }
110 return fn(this.context, () => this.next(state));
111 });
112 }
113 /**
114 * Return the interceptor function or resolve the interceptor function as a binding
115 * from the context
116 *
117 * @param interceptor - Interceptor function or binding key
118 */
119 loadInterceptor(interceptor) {
120 if (typeof interceptor === 'function')
121 return interceptor;
122 debug('Resolving interceptor binding %s', interceptor);
123 return this.context.getValueOrPromise(interceptor);
124 }
125}
126exports.GenericInterceptorChain = GenericInterceptorChain;
127/**
128 * Invoke a chain of interceptors with the context
129 * @param context - Context object
130 * @param interceptors - An array of interceptor functions or binding keys
131 */
132function invokeInterceptors(context, interceptors) {
133 const chain = new GenericInterceptorChain(context, interceptors);
134 return chain.invokeInterceptors();
135}
136exports.invokeInterceptors = invokeInterceptors;
137/**
138 * Compose a list of interceptors as a single interceptor
139 * @param interceptors - A list of interceptor functions or binding keys
140 */
141function composeInterceptors(...interceptors) {
142 return (ctx, next) => {
143 const interceptor = new GenericInterceptorChain(ctx, interceptors).asInterceptor();
144 return interceptor(ctx, next);
145 };
146}
147exports.composeInterceptors = composeInterceptors;
148//# sourceMappingURL=interceptor-chain.js.map
\No newline at end of file