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;
 | |
| 
 | |
| };
 |