All files transport.js

83.13% Statements 69/83
60% Branches 12/20
100% Functions 6/6
83.13% Lines 69/83

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 848x 8x 8x 8x 8x 8x 8x 8x 8x 8x 106x 106x 106x             106x 8x 8x 8x 8x 8x 8x 8x 128x 128x 128x 128x 128x 128x 141x 128x 128x 128x 128x 128x 128x 13x 13x 13x     128x 128x       128x 128x 128x 128x 128x 128x 128x 128x 128x 128x 106x 106x 106x 106x 106x 128x 128x 128x 128x       128x 128x 11x 11x 128x 128x 2x 2x 128x 128x 128x  
// Promise wrapper over wx.request with an SDK side timer, abort support and error mapping.
import { utf8Decode } from './codec.js';
import { fromWxFail, localError } from './errors.js';
import { onAbort } from './abort.js';
import { isArrayBuffer } from './util.js';
 
// wx.request gets a slightly longer timeout than the SDK timer, so a timeout is always reported by
// the SDK timer with one consistent error.
const WX_TIMEOUT_MARGIN_MS = 1000;
 
function responseText(data) {
  if (typeof data === 'string') return data;
  if (data === undefined || data === null) return '';
  if (isArrayBuffer(data)) return utf8Decode(data);
  try {
    return JSON.stringify(data);
  } catch (e) {
    return String(data);
  }
}
 
/**
 * opts: {url, method, header, data, timeoutMs, signal, cancelError: () => YuguError}
 * Resolves {statusCode, header, text} for every HTTP status, rejects with a YuguError on transport
 * failure, timeout (90002) or cancellation.
 */
export function wxRequest(wxApi, opts) {
  return new Promise((resolve, reject) => {
    let settled = false;
    let task = null;
    let timer = null;
    let off = null;
    const finish = (fn, value) => {
      if (settled) return;
      settled = true;
      if (timer) clearTimeout(timer);
      if (off) off();
      fn(value);
    };
    const abortTask = () => {
      try {
        if (task && typeof task.abort === 'function') task.abort();
      } catch (e) {
        // the task may already be finished
      }
    };
    if (opts.signal && opts.signal.aborted) {
      reject(opts.cancelError());
      return;
    }
    try {
      task = wxApi.request({
        url: opts.url,
        method: opts.method,
        header: opts.header,
        data: opts.data,
        timeout: opts.timeoutMs + WX_TIMEOUT_MARGIN_MS,
        dataType: 'text',
        responseType: 'text',
        success: (res) => {
          finish(resolve, {
            statusCode: res && typeof res.statusCode === 'number' ? res.statusCode : 0,
            header: (res && res.header) || {},
            text: responseText(res && res.data),
          });
        },
        fail: (err) => finish(reject, fromWxFail(err, 'request')),
      });
    } catch (e) {
      finish(reject, fromWxFail(e, 'request'));
      return;
    }
    if (settled) return;
    timer = setTimeout(() => {
      abortTask();
      finish(reject, localError(90002, 'request timed out after ' + opts.timeoutMs + ' ms'));
    }, opts.timeoutMs);
    off = onAbort(opts.signal, () => {
      abortTask();
      finish(reject, opts.cancelError());
    });
  });
}