forked from cujojs/rest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
interceptor.js
145 lines (126 loc) · 4.89 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
/*
* Copyright 2012-2016 the original author or authors
* @license MIT, see LICENSE.txt for details
*
* @author Scott Andrews
*/
'use strict';
var defaultClient, mixin, responsePromise, client;
defaultClient = require('./client/default');
mixin = require('./util/mixin');
responsePromise = require('./util/responsePromise');
client = require('./client');
/**
* 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 wrapping
* 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;
}
/**
* 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 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 Promise.resolve((handlers.response || defaultResponseHandler).apply(this, arguments))
.then(Promise.reject.bind(Promise));
};
return function (target, config) {
if (typeof target === 'object') {
config = target;
}
if (typeof target !== 'function') {
target = handlers.client || defaultClient;
}
config = initHandler(config || {});
function interceptedClient(request) {
var context, meta;
context = {};
meta = { 'arguments': Array.prototype.slice.call(arguments), client: interceptedClient };
request = typeof request === 'string' ? { path: request } : request || {};
request.originator = request.originator || interceptedClient;
return responsePromise(
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 || Promise.resolve(request).then(function (request) {
return Promise.resolve(next(request)).then(
function (response) {
return successResponseHandler.call(context, response, config, meta);
},
function (response) {
return errorResponseHandler.call(context, response, config, meta);
}
);
});
return abort ? Promise.race([response, abort]) : response;
},
function (error) {
return Promise.reject({ request: request, error: error });
}
);
}
return client(interceptedClient, target);
};
}
interceptor.ComplexRequest = ComplexRequest;
module.exports = interceptor;