168 lines
5.7 KiB
JavaScript
168 lines
5.7 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
var errors = require('./errors.js'),
|
||
|
isFunction = require('lodash/isFunction'),
|
||
|
isObjectLike = require('lodash/isObjectLike'),
|
||
|
isString = require('lodash/isString'),
|
||
|
isUndefined = require('lodash/isUndefined');
|
||
|
|
||
|
|
||
|
module.exports = function (options) {
|
||
|
|
||
|
var errorText = 'Please verify options'; // For better minification because this string is repeating
|
||
|
|
||
|
if (!isObjectLike(options)) {
|
||
|
throw new TypeError(errorText);
|
||
|
}
|
||
|
|
||
|
if (!isFunction(options.PromiseImpl)) {
|
||
|
throw new TypeError(errorText + '.PromiseImpl');
|
||
|
}
|
||
|
|
||
|
if (!isUndefined(options.constructorMixin) && !isFunction(options.constructorMixin)) {
|
||
|
throw new TypeError(errorText + '.PromiseImpl');
|
||
|
}
|
||
|
|
||
|
var PromiseImpl = options.PromiseImpl;
|
||
|
var constructorMixin = options.constructorMixin;
|
||
|
|
||
|
|
||
|
var plumbing = {};
|
||
|
|
||
|
plumbing.init = function (requestOptions) {
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
self._rp_promise = new PromiseImpl(function (resolve, reject) {
|
||
|
self._rp_resolve = resolve;
|
||
|
self._rp_reject = reject;
|
||
|
if (constructorMixin) {
|
||
|
constructorMixin.apply(self, arguments); // Using arguments since specific Promise libraries may pass additional parameters
|
||
|
}
|
||
|
});
|
||
|
|
||
|
self._rp_callbackOrig = requestOptions.callback;
|
||
|
requestOptions.callback = self.callback = function RP$callback(err, response, body) {
|
||
|
plumbing.callback.call(self, err, response, body);
|
||
|
};
|
||
|
|
||
|
if (isString(requestOptions.method)) {
|
||
|
requestOptions.method = requestOptions.method.toUpperCase();
|
||
|
}
|
||
|
|
||
|
requestOptions.transform = requestOptions.transform || plumbing.defaultTransformations[requestOptions.method];
|
||
|
|
||
|
self._rp_options = requestOptions;
|
||
|
self._rp_options.simple = requestOptions.simple !== false;
|
||
|
self._rp_options.resolveWithFullResponse = requestOptions.resolveWithFullResponse === true;
|
||
|
self._rp_options.transform2xxOnly = requestOptions.transform2xxOnly === true;
|
||
|
|
||
|
};
|
||
|
|
||
|
plumbing.defaultTransformations = {
|
||
|
HEAD: function (body, response, resolveWithFullResponse) {
|
||
|
return resolveWithFullResponse ? response : response.headers;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
plumbing.callback = function (err, response, body) {
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
var origCallbackThrewException = false, thrownException = null;
|
||
|
|
||
|
if (isFunction(self._rp_callbackOrig)) {
|
||
|
try {
|
||
|
self._rp_callbackOrig.apply(self, arguments); // TODO: Apply to self mimics behavior of request@2. Is that also right for request@next?
|
||
|
} catch (e) {
|
||
|
origCallbackThrewException = true;
|
||
|
thrownException = e;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var is2xx = !err && /^2/.test('' + response.statusCode);
|
||
|
|
||
|
if (err) {
|
||
|
|
||
|
self._rp_reject(new errors.RequestError(err, self._rp_options, response));
|
||
|
|
||
|
} else if (self._rp_options.simple && !is2xx) {
|
||
|
|
||
|
if (isFunction(self._rp_options.transform) && self._rp_options.transform2xxOnly === false) {
|
||
|
|
||
|
(new PromiseImpl(function (resolve) {
|
||
|
resolve(self._rp_options.transform(body, response, self._rp_options.resolveWithFullResponse)); // transform may return a Promise
|
||
|
}))
|
||
|
.then(function (transformedResponse) {
|
||
|
self._rp_reject(new errors.StatusCodeError(response.statusCode, body, self._rp_options, transformedResponse));
|
||
|
})
|
||
|
.catch(function (transformErr) {
|
||
|
self._rp_reject(new errors.TransformError(transformErr, self._rp_options, response));
|
||
|
});
|
||
|
|
||
|
} else {
|
||
|
self._rp_reject(new errors.StatusCodeError(response.statusCode, body, self._rp_options, response));
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
if (isFunction(self._rp_options.transform) && (is2xx || self._rp_options.transform2xxOnly === false)) {
|
||
|
|
||
|
(new PromiseImpl(function (resolve) {
|
||
|
resolve(self._rp_options.transform(body, response, self._rp_options.resolveWithFullResponse)); // transform may return a Promise
|
||
|
}))
|
||
|
.then(function (transformedResponse) {
|
||
|
self._rp_resolve(transformedResponse);
|
||
|
})
|
||
|
.catch(function (transformErr) {
|
||
|
self._rp_reject(new errors.TransformError(transformErr, self._rp_options, response));
|
||
|
});
|
||
|
|
||
|
} else if (self._rp_options.resolveWithFullResponse) {
|
||
|
self._rp_resolve(response);
|
||
|
} else {
|
||
|
self._rp_resolve(body);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (origCallbackThrewException) {
|
||
|
throw thrownException;
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
plumbing.exposePromiseMethod = function (exposeTo, bindTo, promisePropertyKey, methodToExpose, exposeAs) {
|
||
|
|
||
|
exposeAs = exposeAs || methodToExpose;
|
||
|
|
||
|
if (exposeAs in exposeTo) {
|
||
|
throw new Error('Unable to expose method "' + exposeAs + '"');
|
||
|
}
|
||
|
|
||
|
exposeTo[exposeAs] = function RP$exposed() {
|
||
|
var self = bindTo || this;
|
||
|
return self[promisePropertyKey][methodToExpose].apply(self[promisePropertyKey], arguments);
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
plumbing.exposePromise = function (exposeTo, bindTo, promisePropertyKey, exposeAs) {
|
||
|
|
||
|
exposeAs = exposeAs || 'promise';
|
||
|
|
||
|
if (exposeAs in exposeTo) {
|
||
|
throw new Error('Unable to expose method "' + exposeAs + '"');
|
||
|
}
|
||
|
|
||
|
exposeTo[exposeAs] = function RP$promise() {
|
||
|
var self = bindTo || this;
|
||
|
return self[promisePropertyKey];
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
return plumbing;
|
||
|
|
||
|
};
|