plumbing.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. 'use strict';
  2. var errors = require('./errors.js'),
  3. isFunction = require('lodash/isFunction'),
  4. isObjectLike = require('lodash/isObjectLike'),
  5. isString = require('lodash/isString'),
  6. isUndefined = require('lodash/isUndefined');
  7. module.exports = function (options) {
  8. var errorText = 'Please verify options'; // For better minification because this string is repeating
  9. if (!isObjectLike(options)) {
  10. throw new TypeError(errorText);
  11. }
  12. if (!isFunction(options.PromiseImpl)) {
  13. throw new TypeError(errorText + '.PromiseImpl');
  14. }
  15. if (!isUndefined(options.constructorMixin) && !isFunction(options.constructorMixin)) {
  16. throw new TypeError(errorText + '.PromiseImpl');
  17. }
  18. var PromiseImpl = options.PromiseImpl;
  19. var constructorMixin = options.constructorMixin;
  20. var plumbing = {};
  21. plumbing.init = function (requestOptions) {
  22. var self = this;
  23. self._rp_promise = new PromiseImpl(function (resolve, reject) {
  24. self._rp_resolve = resolve;
  25. self._rp_reject = reject;
  26. if (constructorMixin) {
  27. constructorMixin.apply(self, arguments); // Using arguments since specific Promise libraries may pass additional parameters
  28. }
  29. });
  30. self._rp_callbackOrig = requestOptions.callback;
  31. requestOptions.callback = self.callback = function RP$callback(err, response, body) {
  32. plumbing.callback.call(self, err, response, body);
  33. };
  34. if (isString(requestOptions.method)) {
  35. requestOptions.method = requestOptions.method.toUpperCase();
  36. }
  37. requestOptions.transform = requestOptions.transform || plumbing.defaultTransformations[requestOptions.method];
  38. self._rp_options = requestOptions;
  39. self._rp_options.simple = requestOptions.simple !== false;
  40. self._rp_options.resolveWithFullResponse = requestOptions.resolveWithFullResponse === true;
  41. self._rp_options.transform2xxOnly = requestOptions.transform2xxOnly === true;
  42. };
  43. plumbing.defaultTransformations = {
  44. HEAD: function (body, response, resolveWithFullResponse) {
  45. return resolveWithFullResponse ? response : response.headers;
  46. }
  47. };
  48. plumbing.callback = function (err, response, body) {
  49. var self = this;
  50. var origCallbackThrewException = false, thrownException = null;
  51. if (isFunction(self._rp_callbackOrig)) {
  52. try {
  53. self._rp_callbackOrig.apply(self, arguments); // TODO: Apply to self mimics behavior of request@2. Is that also right for request@next?
  54. } catch (e) {
  55. origCallbackThrewException = true;
  56. thrownException = e;
  57. }
  58. }
  59. var is2xx = !err && /^2/.test('' + response.statusCode);
  60. if (err) {
  61. self._rp_reject(new errors.RequestError(err, self._rp_options, response));
  62. } else if (self._rp_options.simple && !is2xx) {
  63. if (isFunction(self._rp_options.transform) && self._rp_options.transform2xxOnly === false) {
  64. (new PromiseImpl(function (resolve) {
  65. resolve(self._rp_options.transform(body, response, self._rp_options.resolveWithFullResponse)); // transform may return a Promise
  66. }))
  67. .then(function (transformedResponse) {
  68. self._rp_reject(new errors.StatusCodeError(response.statusCode, body, self._rp_options, transformedResponse));
  69. })
  70. .catch(function (transformErr) {
  71. self._rp_reject(new errors.TransformError(transformErr, self._rp_options, response));
  72. });
  73. } else {
  74. self._rp_reject(new errors.StatusCodeError(response.statusCode, body, self._rp_options, response));
  75. }
  76. } else {
  77. if (isFunction(self._rp_options.transform) && (is2xx || self._rp_options.transform2xxOnly === false)) {
  78. (new PromiseImpl(function (resolve) {
  79. resolve(self._rp_options.transform(body, response, self._rp_options.resolveWithFullResponse)); // transform may return a Promise
  80. }))
  81. .then(function (transformedResponse) {
  82. self._rp_resolve(transformedResponse);
  83. })
  84. .catch(function (transformErr) {
  85. self._rp_reject(new errors.TransformError(transformErr, self._rp_options, response));
  86. });
  87. } else if (self._rp_options.resolveWithFullResponse) {
  88. self._rp_resolve(response);
  89. } else {
  90. self._rp_resolve(body);
  91. }
  92. }
  93. if (origCallbackThrewException) {
  94. throw thrownException;
  95. }
  96. };
  97. plumbing.exposePromiseMethod = function (exposeTo, bindTo, promisePropertyKey, methodToExpose, exposeAs) {
  98. exposeAs = exposeAs || methodToExpose;
  99. if (exposeAs in exposeTo) {
  100. throw new Error('Unable to expose method "' + exposeAs + '"');
  101. }
  102. exposeTo[exposeAs] = function RP$exposed() {
  103. var self = bindTo || this;
  104. return self[promisePropertyKey][methodToExpose].apply(self[promisePropertyKey], arguments);
  105. };
  106. };
  107. plumbing.exposePromise = function (exposeTo, bindTo, promisePropertyKey, exposeAs) {
  108. exposeAs = exposeAs || 'promise';
  109. if (exposeAs in exposeTo) {
  110. throw new Error('Unable to expose method "' + exposeAs + '"');
  111. }
  112. exposeTo[exposeAs] = function RP$promise() {
  113. var self = bindTo || this;
  114. return self[promisePropertyKey];
  115. };
  116. };
  117. return plumbing;
  118. };