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