UNPKG

5.71 kBJavaScriptView Raw
1/*
2 * Copyright 2012-2015 the original author or authors
3 * @license MIT, see LICENSE.txt for details
4 *
5 * @author Scott Andrews
6 */
7
8(function (define) {
9 'use strict';
10
11 define(function (require) {
12
13 var defaultClient, mixin, responsePromise, client, when;
14
15 defaultClient = require('./client/default');
16 mixin = require('./util/mixin');
17 responsePromise = require('./util/responsePromise');
18 client = require('./client');
19 when = require('when');
20
21 /**
22 * Interceptors have the ability to intercept the request and/org response
23 * objects. They may augment, prune, transform or replace the
24 * request/response as needed. Clients may be composed by wrapping
25 * together multiple interceptors.
26 *
27 * Configured interceptors are functional in nature. Wrapping a client in
28 * an interceptor will not affect the client, merely the data that flows in
29 * and out of that client. A common configuration can be created once and
30 * shared; specialization can be created by further wrapping that client
31 * with custom interceptors.
32 *
33 * @param {Client} [target] client to wrap
34 * @param {Object} [config] configuration for the interceptor, properties will be specific to the interceptor implementation
35 * @returns {Client} A client wrapped with the interceptor
36 *
37 * @class Interceptor
38 */
39
40 function defaultInitHandler(config) {
41 return config;
42 }
43
44 function defaultRequestHandler(request /*, config, meta */) {
45 return request;
46 }
47
48 function defaultResponseHandler(response /*, config, meta */) {
49 return response;
50 }
51
52 function race(promisesOrValues) {
53 // this function is different than when.any as the first to reject also wins
54 return when.promise(function (resolve, reject) {
55 promisesOrValues.forEach(function (promiseOrValue) {
56 when(promiseOrValue, resolve, reject);
57 });
58 });
59 }
60
61 /**
62 * Alternate return type for the request handler that allows for more complex interactions.
63 *
64 * @param properties.request the traditional request return object
65 * @param {Promise} [properties.abort] promise that resolves if/when the request is aborted
66 * @param {Client} [properties.client] override the defined client with an alternate client
67 * @param [properties.response] response for the request, short circuit the request
68 */
69 function ComplexRequest(properties) {
70 if (!(this instanceof ComplexRequest)) {
71 // in case users forget the 'new' don't mix into the interceptor
72 return new ComplexRequest(properties);
73 }
74 mixin(this, properties);
75 }
76
77 /**
78 * Create a new interceptor for the provided handlers.
79 *
80 * @param {Function} [handlers.init] one time intialization, must return the config object
81 * @param {Function} [handlers.request] request handler
82 * @param {Function} [handlers.response] response handler regardless of error state
83 * @param {Function} [handlers.success] response handler when the request is not in error
84 * @param {Function} [handlers.error] response handler when the request is in error, may be used to 'unreject' an error state
85 * @param {Function} [handlers.client] the client to use if otherwise not specified, defaults to platform default client
86 *
87 * @returns {Interceptor}
88 */
89 function interceptor(handlers) {
90
91 var initHandler, requestHandler, successResponseHandler, errorResponseHandler;
92
93 handlers = handlers || {};
94
95 initHandler = handlers.init || defaultInitHandler;
96 requestHandler = handlers.request || defaultRequestHandler;
97 successResponseHandler = handlers.success || handlers.response || defaultResponseHandler;
98 errorResponseHandler = handlers.error || function () {
99 // Propagate the rejection, with the result of the handler
100 return when((handlers.response || defaultResponseHandler).apply(this, arguments), when.reject, when.reject);
101 };
102
103 return function (target, config) {
104
105 if (typeof target === 'object') {
106 config = target;
107 }
108 if (typeof target !== 'function') {
109 target = handlers.client || defaultClient;
110 }
111
112 config = initHandler(config || {});
113
114 function interceptedClient(request) {
115 var context, meta;
116 context = {};
117 meta = { 'arguments': Array.prototype.slice.call(arguments), client: interceptedClient };
118 request = typeof request === 'string' ? { path: request } : request || {};
119 request.originator = request.originator || interceptedClient;
120 return responsePromise(
121 requestHandler.call(context, request, config, meta),
122 function (request) {
123 var response, abort, next;
124 next = target;
125 if (request instanceof ComplexRequest) {
126 // unpack request
127 abort = request.abort;
128 next = request.client || next;
129 response = request.response;
130 // normalize request, must be last
131 request = request.request;
132 }
133 response = response || when(request, function (request) {
134 return when(
135 next(request),
136 function (response) {
137 return successResponseHandler.call(context, response, config, meta);
138 },
139 function (response) {
140 return errorResponseHandler.call(context, response, config, meta);
141 }
142 );
143 });
144 return abort ? race([response, abort]) : response;
145 },
146 function (error) {
147 return when.reject({ request: request, error: error });
148 }
149 );
150 }
151
152 return client(interceptedClient, target);
153 };
154 }
155
156 interceptor.ComplexRequest = ComplexRequest;
157
158 return interceptor;
159
160 });
161
162}(
163 typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }
164 // Boilerplate for AMD and Node
165));