[d24f17c] | 1 | "use strict";
|
---|
| 2 |
|
---|
| 3 | var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
---|
| 4 | exports.__esModule = true;
|
---|
| 5 | exports.default = http;
|
---|
| 6 | exports.encodeFormOrQuery = encodeFormOrQuery;
|
---|
| 7 | exports.isFile = isFile;
|
---|
| 8 | exports.makeHttp = makeHttp;
|
---|
| 9 | exports.mergeInQueryOrForm = mergeInQueryOrForm;
|
---|
| 10 | exports.self = void 0;
|
---|
| 11 | exports.serializeHeaders = serializeHeaders;
|
---|
| 12 | exports.serializeRes = serializeRes;
|
---|
| 13 | exports.shouldDownloadAsText = void 0;
|
---|
| 14 | var _qs = _interopRequireDefault(require("qs"));
|
---|
| 15 | var _jsYaml = _interopRequireDefault(require("js-yaml"));
|
---|
| 16 | require("../helpers/fetch-polyfill.node.js");
|
---|
| 17 | var _styleSerializer = require("../execute/oas3/style-serializer.js");
|
---|
| 18 | // For testing
|
---|
| 19 | const self = exports.self = {
|
---|
| 20 | serializeRes,
|
---|
| 21 | mergeInQueryOrForm
|
---|
| 22 | };
|
---|
| 23 |
|
---|
| 24 | // Handles fetch-like syntax and the case where there is only one object passed-in
|
---|
| 25 | // (which will have the URL as a property). Also serializes the response.
|
---|
| 26 | async function http(url, request = {}) {
|
---|
| 27 | if (typeof url === 'object') {
|
---|
| 28 | request = url;
|
---|
| 29 | url = request.url;
|
---|
| 30 | }
|
---|
| 31 | request.headers = request.headers || {};
|
---|
| 32 |
|
---|
| 33 | // Serializes query, for convenience
|
---|
| 34 | // Should be the last thing we do, as its hard to mutate the URL with
|
---|
| 35 | // the search string, but much easier to manipulate the req.query object
|
---|
| 36 | self.mergeInQueryOrForm(request);
|
---|
| 37 |
|
---|
| 38 | // Newlines in header values cause weird error messages from `window.fetch`,
|
---|
| 39 | // so let's message them out.
|
---|
| 40 | // Context: https://stackoverflow.com/a/50709178
|
---|
| 41 | if (request.headers) {
|
---|
| 42 | Object.keys(request.headers).forEach(headerName => {
|
---|
| 43 | const value = request.headers[headerName];
|
---|
| 44 | if (typeof value === 'string') {
|
---|
| 45 | request.headers[headerName] = value.replace(/\n+/g, ' ');
|
---|
| 46 | }
|
---|
| 47 | });
|
---|
| 48 | }
|
---|
| 49 |
|
---|
| 50 | // Wait for the request interceptor, if it was provided
|
---|
| 51 | // WARNING: don't put anything between this and the request firing unless
|
---|
| 52 | // you have a good reason!
|
---|
| 53 | if (request.requestInterceptor) {
|
---|
| 54 | request = (await request.requestInterceptor(request)) || request;
|
---|
| 55 | }
|
---|
| 56 |
|
---|
| 57 | /**
|
---|
| 58 | * For content-type=multipart/form-data remove content-type from request before fetch,
|
---|
| 59 | * so that correct one with `boundary` is set when request body is different from boundary encoded string.
|
---|
| 60 | */
|
---|
| 61 | const contentType = request.headers['content-type'] || request.headers['Content-Type'];
|
---|
| 62 | if (/multipart\/form-data/i.test(contentType)) {
|
---|
| 63 | delete request.headers['content-type'];
|
---|
| 64 | delete request.headers['Content-Type'];
|
---|
| 65 | }
|
---|
| 66 |
|
---|
| 67 | // eslint-disable-next-line no-undef
|
---|
| 68 | let res;
|
---|
| 69 | try {
|
---|
| 70 | res = await (request.userFetch || fetch)(request.url, request);
|
---|
| 71 | res = await self.serializeRes(res, url, request);
|
---|
| 72 | if (request.responseInterceptor) {
|
---|
| 73 | res = (await request.responseInterceptor(res)) || res;
|
---|
| 74 | }
|
---|
| 75 | } catch (resError) {
|
---|
| 76 | if (!res) {
|
---|
| 77 | // res is completely absent, so we can't construct our own error
|
---|
| 78 | // so we'll just throw the error we got
|
---|
| 79 | throw resError;
|
---|
| 80 | }
|
---|
| 81 | const error = new Error(res.statusText || `response status is ${res.status}`);
|
---|
| 82 | error.status = res.status;
|
---|
| 83 | error.statusCode = res.status;
|
---|
| 84 | error.responseError = resError;
|
---|
| 85 | throw error;
|
---|
| 86 | }
|
---|
| 87 | if (!res.ok) {
|
---|
| 88 | const error = new Error(res.statusText || `response status is ${res.status}`);
|
---|
| 89 | error.status = res.status;
|
---|
| 90 | error.statusCode = res.status;
|
---|
| 91 | error.response = res;
|
---|
| 92 | throw error;
|
---|
| 93 | }
|
---|
| 94 | return res;
|
---|
| 95 | }
|
---|
| 96 |
|
---|
| 97 | // exported for testing
|
---|
| 98 | const shouldDownloadAsText = (contentType = '') => /(json|xml|yaml|text)\b/.test(contentType);
|
---|
| 99 | exports.shouldDownloadAsText = shouldDownloadAsText;
|
---|
| 100 | function parseBody(body, contentType) {
|
---|
| 101 | if (contentType && (contentType.indexOf('application/json') === 0 || contentType.indexOf('+json') > 0)) {
|
---|
| 102 | return JSON.parse(body);
|
---|
| 103 | }
|
---|
| 104 | return _jsYaml.default.load(body);
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | // Serialize the response, returns a promise with headers and the body part of the hash
|
---|
| 108 | function serializeRes(oriRes, url, {
|
---|
| 109 | loadSpec = false
|
---|
| 110 | } = {}) {
|
---|
| 111 | const res = {
|
---|
| 112 | ok: oriRes.ok,
|
---|
| 113 | url: oriRes.url || url,
|
---|
| 114 | status: oriRes.status,
|
---|
| 115 | statusText: oriRes.statusText,
|
---|
| 116 | headers: serializeHeaders(oriRes.headers)
|
---|
| 117 | };
|
---|
| 118 | const contentType = res.headers['content-type'];
|
---|
| 119 | const useText = loadSpec || shouldDownloadAsText(contentType);
|
---|
| 120 | const getBody = useText ? oriRes.text : oriRes.blob || oriRes.buffer;
|
---|
| 121 | return getBody.call(oriRes).then(body => {
|
---|
| 122 | res.text = body;
|
---|
| 123 | res.data = body;
|
---|
| 124 | if (useText) {
|
---|
| 125 | try {
|
---|
| 126 | const obj = parseBody(body, contentType);
|
---|
| 127 | res.body = obj;
|
---|
| 128 | res.obj = obj;
|
---|
| 129 | } catch (e) {
|
---|
| 130 | res.parseError = e;
|
---|
| 131 | }
|
---|
| 132 | }
|
---|
| 133 | return res;
|
---|
| 134 | });
|
---|
| 135 | }
|
---|
| 136 | function serializeHeaderValue(value) {
|
---|
| 137 | const isMulti = value.includes(', ');
|
---|
| 138 | return isMulti ? value.split(', ') : value;
|
---|
| 139 | }
|
---|
| 140 |
|
---|
| 141 | // Serialize headers into a hash, where mutliple-headers result in an array.
|
---|
| 142 | //
|
---|
| 143 | // eg: Cookie: one
|
---|
| 144 | // Cookie: two
|
---|
| 145 | // = { Cookie: [ "one", "two" ]
|
---|
| 146 | function serializeHeaders(headers = {}) {
|
---|
| 147 | if (typeof headers.entries !== 'function') return {};
|
---|
| 148 | return Array.from(headers.entries()).reduce((acc, [header, value]) => {
|
---|
| 149 | acc[header] = serializeHeaderValue(value);
|
---|
| 150 | return acc;
|
---|
| 151 | }, {});
|
---|
| 152 | }
|
---|
| 153 | function isFile(obj, navigatorObj) {
|
---|
| 154 | if (!navigatorObj && typeof navigator !== 'undefined') {
|
---|
| 155 | // eslint-disable-next-line no-undef
|
---|
| 156 | navigatorObj = navigator;
|
---|
| 157 | }
|
---|
| 158 | if (navigatorObj && navigatorObj.product === 'ReactNative') {
|
---|
| 159 | if (obj && typeof obj === 'object' && typeof obj.uri === 'string') {
|
---|
| 160 | return true;
|
---|
| 161 | }
|
---|
| 162 | return false;
|
---|
| 163 | }
|
---|
| 164 | if (typeof File !== 'undefined' && obj instanceof File) {
|
---|
| 165 | return true;
|
---|
| 166 | }
|
---|
| 167 | if (typeof Blob !== 'undefined' && obj instanceof Blob) {
|
---|
| 168 | return true;
|
---|
| 169 | }
|
---|
| 170 | if (ArrayBuffer.isView(obj)) {
|
---|
| 171 | return true;
|
---|
| 172 | }
|
---|
| 173 | return obj !== null && typeof obj === 'object' && typeof obj.pipe === 'function';
|
---|
| 174 | }
|
---|
| 175 | function isArrayOfFile(obj, navigatorObj) {
|
---|
| 176 | return Array.isArray(obj) && obj.some(v => isFile(v, navigatorObj));
|
---|
| 177 | }
|
---|
| 178 | const STYLE_SEPARATORS = {
|
---|
| 179 | form: ',',
|
---|
| 180 | spaceDelimited: '%20',
|
---|
| 181 | pipeDelimited: '|'
|
---|
| 182 | };
|
---|
| 183 | const SEPARATORS = {
|
---|
| 184 | csv: ',',
|
---|
| 185 | ssv: '%20',
|
---|
| 186 | tsv: '%09',
|
---|
| 187 | pipes: '|'
|
---|
| 188 | };
|
---|
| 189 |
|
---|
| 190 | /**
|
---|
| 191 | * Specialized sub-class of File class, that only
|
---|
| 192 | * accepts string data and retain this data in `data`
|
---|
| 193 | * public property throughout the lifecycle of its instances.
|
---|
| 194 | *
|
---|
| 195 | * This sub-class is exclusively used only when Encoding Object
|
---|
| 196 | * is defined within the Media Type Object (OpenAPI 3.x.y).
|
---|
| 197 | */
|
---|
| 198 | class FileWithData extends File {
|
---|
| 199 | constructor(data, name = '', options = {}) {
|
---|
| 200 | super([data], name, options);
|
---|
| 201 | this.data = data;
|
---|
| 202 | }
|
---|
| 203 | valueOf() {
|
---|
| 204 | return this.data;
|
---|
| 205 | }
|
---|
| 206 | toString() {
|
---|
| 207 | return this.valueOf();
|
---|
| 208 | }
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 | // Formats a key-value and returns an array of key-value pairs.
|
---|
| 212 | //
|
---|
| 213 | // Return value example 1: [['color', 'blue']]
|
---|
| 214 | // Return value example 2: [['color', 'blue,black,brown']]
|
---|
| 215 | // Return value example 3: [['color', ['blue', 'black', 'brown']]]
|
---|
| 216 | // Return value example 4: [['color', 'R,100,G,200,B,150']]
|
---|
| 217 | // Return value example 5: [['R', '100'], ['G', '200'], ['B', '150']]
|
---|
| 218 | // Return value example 6: [['color[R]', '100'], ['color[G]', '200'], ['color[B]', '150']]
|
---|
| 219 | function formatKeyValue(key, input, skipEncoding = false) {
|
---|
| 220 | const {
|
---|
| 221 | collectionFormat,
|
---|
| 222 | allowEmptyValue,
|
---|
| 223 | serializationOption,
|
---|
| 224 | encoding
|
---|
| 225 | } = input;
|
---|
| 226 | // `input` can be string
|
---|
| 227 | const value = typeof input === 'object' && !Array.isArray(input) ? input.value : input;
|
---|
| 228 | const encodeFn = skipEncoding ? k => k.toString() : k => encodeURIComponent(k);
|
---|
| 229 | const encodedKey = encodeFn(key);
|
---|
| 230 | if (typeof value === 'undefined' && allowEmptyValue) {
|
---|
| 231 | return [[encodedKey, '']];
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | // file
|
---|
| 235 | if (isFile(value) || isArrayOfFile(value)) {
|
---|
| 236 | return [[encodedKey, value]];
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | // for OAS 3 Parameter Object for serialization
|
---|
| 240 | if (serializationOption) {
|
---|
| 241 | return formatKeyValueBySerializationOption(key, value, skipEncoding, serializationOption);
|
---|
| 242 | }
|
---|
| 243 |
|
---|
| 244 | // for OAS 3 Encoding Object
|
---|
| 245 | if (encoding) {
|
---|
| 246 | if ([typeof encoding.style, typeof encoding.explode, typeof encoding.allowReserved].some(type => type !== 'undefined')) {
|
---|
| 247 | const {
|
---|
| 248 | style,
|
---|
| 249 | explode,
|
---|
| 250 | allowReserved
|
---|
| 251 | } = encoding;
|
---|
| 252 | return formatKeyValueBySerializationOption(key, value, skipEncoding, {
|
---|
| 253 | style,
|
---|
| 254 | explode,
|
---|
| 255 | allowReserved
|
---|
| 256 | });
|
---|
| 257 | }
|
---|
| 258 | if (typeof encoding.contentType === 'string') {
|
---|
| 259 | if (encoding.contentType.startsWith('application/json')) {
|
---|
| 260 | // if value is a string, assume value is already a JSON string
|
---|
| 261 | const json = typeof value === 'string' ? value : JSON.stringify(value);
|
---|
| 262 | const encodedJson = encodeFn(json);
|
---|
| 263 | const file = new FileWithData(encodedJson, 'blob', {
|
---|
| 264 | type: encoding.contentType
|
---|
| 265 | });
|
---|
| 266 | return [[encodedKey, file]];
|
---|
| 267 | }
|
---|
| 268 | const encodedData = encodeFn(String(value));
|
---|
| 269 | const blob = new FileWithData(encodedData, 'blob', {
|
---|
| 270 | type: encoding.contentType
|
---|
| 271 | });
|
---|
| 272 | return [[encodedKey, blob]];
|
---|
| 273 | }
|
---|
| 274 |
|
---|
| 275 | // Primitive
|
---|
| 276 | if (typeof value !== 'object') {
|
---|
| 277 | return [[encodedKey, encodeFn(value)]];
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | // Array of primitives
|
---|
| 281 | if (Array.isArray(value) && value.every(v => typeof v !== 'object')) {
|
---|
| 282 | return [[encodedKey, value.map(encodeFn).join(',')]];
|
---|
| 283 | }
|
---|
| 284 |
|
---|
| 285 | // Array or object
|
---|
| 286 | return [[encodedKey, encodeFn(JSON.stringify(value))]];
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | // for OAS 2 Parameter Object
|
---|
| 290 | // Primitive
|
---|
| 291 | if (typeof value !== 'object') {
|
---|
| 292 | return [[encodedKey, encodeFn(value)]];
|
---|
| 293 | }
|
---|
| 294 |
|
---|
| 295 | // Array
|
---|
| 296 | if (Array.isArray(value)) {
|
---|
| 297 | if (collectionFormat === 'multi') {
|
---|
| 298 | // In case of multipart/formdata, it is used as array.
|
---|
| 299 | // Otherwise, the caller will convert it to a query by qs.stringify.
|
---|
| 300 | return [[encodedKey, value.map(encodeFn)]];
|
---|
| 301 | }
|
---|
| 302 | return [[encodedKey, value.map(encodeFn).join(SEPARATORS[collectionFormat || 'csv'])]];
|
---|
| 303 | }
|
---|
| 304 |
|
---|
| 305 | // Object
|
---|
| 306 | return [[encodedKey, '']];
|
---|
| 307 | }
|
---|
| 308 | function formatKeyValueBySerializationOption(key, value, skipEncoding, serializationOption) {
|
---|
| 309 | const style = serializationOption.style || 'form';
|
---|
| 310 | const explode = typeof serializationOption.explode === 'undefined' ? style === 'form' : serializationOption.explode;
|
---|
| 311 | // eslint-disable-next-line no-nested-ternary
|
---|
| 312 | const escape = skipEncoding ? false : serializationOption && serializationOption.allowReserved ? 'unsafe' : 'reserved';
|
---|
| 313 | const encodeFn = v => (0, _styleSerializer.encodeDisallowedCharacters)(v, {
|
---|
| 314 | escape
|
---|
| 315 | });
|
---|
| 316 | const encodeKeyFn = skipEncoding ? k => k : k => (0, _styleSerializer.encodeDisallowedCharacters)(k, {
|
---|
| 317 | escape
|
---|
| 318 | });
|
---|
| 319 |
|
---|
| 320 | // Primitive
|
---|
| 321 | if (typeof value !== 'object') {
|
---|
| 322 | return [[encodeKeyFn(key), encodeFn(value)]];
|
---|
| 323 | }
|
---|
| 324 |
|
---|
| 325 | // Array
|
---|
| 326 | if (Array.isArray(value)) {
|
---|
| 327 | if (explode) {
|
---|
| 328 | // In case of multipart/formdata, it is used as array.
|
---|
| 329 | // Otherwise, the caller will convert it to a query by qs.stringify.
|
---|
| 330 | return [[encodeKeyFn(key), value.map(encodeFn)]];
|
---|
| 331 | }
|
---|
| 332 | return [[encodeKeyFn(key), value.map(encodeFn).join(STYLE_SEPARATORS[style])]];
|
---|
| 333 | }
|
---|
| 334 |
|
---|
| 335 | // Object
|
---|
| 336 | if (style === 'deepObject') {
|
---|
| 337 | return Object.keys(value).map(valueKey => [encodeKeyFn(`${key}[${valueKey}]`), encodeFn(value[valueKey])]);
|
---|
| 338 | }
|
---|
| 339 | if (explode) {
|
---|
| 340 | return Object.keys(value).map(valueKey => [encodeKeyFn(valueKey), encodeFn(value[valueKey])]);
|
---|
| 341 | }
|
---|
| 342 | return [[encodeKeyFn(key), Object.keys(value).map(valueKey => [`${encodeKeyFn(valueKey)},${encodeFn(value[valueKey])}`]).join(',')]];
|
---|
| 343 | }
|
---|
| 344 | function buildFormData(reqForm) {
|
---|
| 345 | /**
|
---|
| 346 | * Build a new FormData instance, support array as field value
|
---|
| 347 | * OAS2.0 - when collectionFormat is multi
|
---|
| 348 | * OAS3.0 - when explode of Encoding Object is true
|
---|
| 349 | *
|
---|
| 350 | * This function explicitly handles Buffers (for backward compatibility)
|
---|
| 351 | * if provided as a values to FormData. FormData can only handle USVString
|
---|
| 352 | * or Blob.
|
---|
| 353 | *
|
---|
| 354 | * @param {Object} reqForm - ori req.form
|
---|
| 355 | * @return {FormData} - new FormData instance
|
---|
| 356 | */
|
---|
| 357 | return Object.entries(reqForm).reduce((formData, [name, input]) => {
|
---|
| 358 | // eslint-disable-next-line no-restricted-syntax
|
---|
| 359 | for (const [key, value] of formatKeyValue(name, input, true)) {
|
---|
| 360 | if (Array.isArray(value)) {
|
---|
| 361 | // eslint-disable-next-line no-restricted-syntax
|
---|
| 362 | for (const v of value) {
|
---|
| 363 | if (ArrayBuffer.isView(v)) {
|
---|
| 364 | const blob = new Blob([v]);
|
---|
| 365 | formData.append(key, blob);
|
---|
| 366 | } else {
|
---|
| 367 | formData.append(key, v);
|
---|
| 368 | }
|
---|
| 369 | }
|
---|
| 370 | } else if (ArrayBuffer.isView(value)) {
|
---|
| 371 | const blob = new Blob([value]);
|
---|
| 372 | formData.append(key, blob);
|
---|
| 373 | } else {
|
---|
| 374 | formData.append(key, value);
|
---|
| 375 | }
|
---|
| 376 | }
|
---|
| 377 | return formData;
|
---|
| 378 | }, new FormData());
|
---|
| 379 | }
|
---|
| 380 |
|
---|
| 381 | // Encodes an object using appropriate serializer.
|
---|
| 382 | function encodeFormOrQuery(data) {
|
---|
| 383 | /**
|
---|
| 384 | * Encode parameter names and values
|
---|
| 385 | * @param {Object} result - parameter names and values
|
---|
| 386 | * @param {string} parameterName - Parameter name
|
---|
| 387 | * @return {object} encoded parameter names and values
|
---|
| 388 | */
|
---|
| 389 | const encodedQuery = Object.keys(data).reduce((result, parameterName) => {
|
---|
| 390 | // eslint-disable-next-line no-restricted-syntax
|
---|
| 391 | for (const [key, value] of formatKeyValue(parameterName, data[parameterName])) {
|
---|
| 392 | if (value instanceof FileWithData) {
|
---|
| 393 | result[key] = value.valueOf();
|
---|
| 394 | } else {
|
---|
| 395 | result[key] = value;
|
---|
| 396 | }
|
---|
| 397 | }
|
---|
| 398 | return result;
|
---|
| 399 | }, {});
|
---|
| 400 | return _qs.default.stringify(encodedQuery, {
|
---|
| 401 | encode: false,
|
---|
| 402 | indices: false
|
---|
| 403 | }) || '';
|
---|
| 404 | }
|
---|
| 405 |
|
---|
| 406 | // If the request has a `query` object, merge it into the request.url, and delete the object
|
---|
| 407 | // If file and/or multipart, also create FormData instance
|
---|
| 408 | function mergeInQueryOrForm(req = {}) {
|
---|
| 409 | const {
|
---|
| 410 | url = '',
|
---|
| 411 | query,
|
---|
| 412 | form
|
---|
| 413 | } = req;
|
---|
| 414 | const joinSearch = (...strs) => {
|
---|
| 415 | const search = strs.filter(a => a).join('&'); // Only truthy value
|
---|
| 416 | return search ? `?${search}` : ''; // Only add '?' if there is a str
|
---|
| 417 | };
|
---|
| 418 | if (form) {
|
---|
| 419 | const hasFile = Object.keys(form).some(key => {
|
---|
| 420 | const {
|
---|
| 421 | value
|
---|
| 422 | } = form[key];
|
---|
| 423 | return isFile(value) || isArrayOfFile(value);
|
---|
| 424 | });
|
---|
| 425 | const contentType = req.headers['content-type'] || req.headers['Content-Type'];
|
---|
| 426 | if (hasFile || /multipart\/form-data/i.test(contentType)) {
|
---|
| 427 | const formdata = buildFormData(req.form);
|
---|
| 428 | req.formdata = formdata;
|
---|
| 429 | req.body = formdata;
|
---|
| 430 | } else {
|
---|
| 431 | req.body = encodeFormOrQuery(form);
|
---|
| 432 | }
|
---|
| 433 | delete req.form;
|
---|
| 434 | }
|
---|
| 435 | if (query) {
|
---|
| 436 | const [baseUrl, oriSearch] = url.split('?');
|
---|
| 437 | let newStr = '';
|
---|
| 438 | if (oriSearch) {
|
---|
| 439 | const oriQuery = _qs.default.parse(oriSearch);
|
---|
| 440 | const keysToRemove = Object.keys(query);
|
---|
| 441 | keysToRemove.forEach(key => delete oriQuery[key]);
|
---|
| 442 | newStr = _qs.default.stringify(oriQuery, {
|
---|
| 443 | encode: true
|
---|
| 444 | });
|
---|
| 445 | }
|
---|
| 446 | const finalStr = joinSearch(newStr, encodeFormOrQuery(query));
|
---|
| 447 | req.url = baseUrl + finalStr;
|
---|
| 448 | delete req.query;
|
---|
| 449 | }
|
---|
| 450 | return req;
|
---|
| 451 | }
|
---|
| 452 |
|
---|
| 453 | // Wrap a http function ( there are otherways to do this, consider this deprecated )
|
---|
| 454 | function makeHttp(httpFn, preFetch, postFetch) {
|
---|
| 455 | postFetch = postFetch || (a => a);
|
---|
| 456 | preFetch = preFetch || (a => a);
|
---|
| 457 | return req => {
|
---|
| 458 | if (typeof req === 'string') {
|
---|
| 459 | req = {
|
---|
| 460 | url: req
|
---|
| 461 | };
|
---|
| 462 | }
|
---|
| 463 | self.mergeInQueryOrForm(req);
|
---|
| 464 | req = preFetch(req);
|
---|
| 465 | return postFetch(httpFn(req));
|
---|
| 466 | };
|
---|
| 467 | } |
---|