All files / src/utils http.ts

87.5% Statements 28/32
58.82% Branches 10/17
100% Functions 2/2
87.5% Lines 28/32

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 7933x 33x 33x   33x   33x 33x 33x 33x   33x                         33x 1234x   1234x 860x 860x 1684x   1684x 833x         1234x 1234x 1234x 1234x 1234x   1193x   41x 41x       41x                                   1234x 5x                
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
import { Dates, Objects, Strings, System } from 'cafe-utility'
import { BeeRequestOptions, BeeResponseError } from '../index'
 
const { AxiosError } = axios
 
const MAX_FAILED_ATTEMPTS = 100_000
const DELAY_FAST = 200
const DELAY_SLOW = 1000
const DELAY_THRESHOLD = Dates.minutes(1) / DELAY_FAST
 
export const DEFAULT_HTTP_CONFIG: AxiosRequestConfig = {
  headers: {
    accept: 'application/json, text/plain, */*',
  },
  maxBodyLength: Infinity,
  maxContentLength: Infinity,
}
 
/**
 * Main function to make HTTP requests.
 * @param options User defined settings
 * @param config Internal settings and/or Bee settings
 */
export async function http<T>(options: BeeRequestOptions, config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
  const requestConfig: AxiosRequestConfig = Objects.deepMerge3(DEFAULT_HTTP_CONFIG, config, options)
 
  if (requestConfig.params) {
    const keys = Object.keys(requestConfig.params)
    for (const key of keys) {
      const value = requestConfig.params[key]
 
      if (value === undefined) {
        delete requestConfig.params[key]
      }
    }
  }
 
  let failedAttempts = 0
  while (failedAttempts < MAX_FAILED_ATTEMPTS) {
    try {
      maybeRunOnRequestHook(options, requestConfig)
      const response = await axios(requestConfig)
 
      return response as AxiosResponse<T>
    } catch (e: unknown) {
      if (e instanceof AxiosError) {
        Iif (e.code === 'ECONNABORTED' && options.endlesslyRetry) {
          failedAttempts++
          await System.sleepMillis(failedAttempts < DELAY_THRESHOLD ? DELAY_FAST : DELAY_SLOW)
        } else {
          throw new BeeResponseError(
            config.method || 'get',
            config.url || '<unknown>',
            e.message,
            e.response?.data,
            e.response?.status,
            e.code,
          )
        }
      } else E{
        throw e
      }
    }
  }
  throw Error('Max number of failed attempts reached')
}
 
function maybeRunOnRequestHook(options: BeeRequestOptions, requestConfig: AxiosRequestConfig) {
  if (options.onRequest) {
    options.onRequest({
      method: requestConfig.method || 'GET',
      url: Strings.joinUrl(requestConfig.baseURL as string, requestConfig.url as string),
      headers: { ...requestConfig.headers } as Record<string, string>,
      params: requestConfig.params,
    })
  }
}