Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/request / src / adapters / fetch.ts
Size: Mime:
import { URL, URLSearchParams } from 'url'
import { EMPTY_OBJ, isObj } from 'exotic'
import {
  OneRequestStore,
  ScopedRequest,
  Params,
  ParamsObj,
  RequestBody,
  ResponseExtended,
} from '../typings'
import { config } from '../config'
import { fromRequestToSerialized } from '../deps/fromRequestToSerialized'
import { encodeBody } from '../deps/encodeBody'
import { fromParamsToStringified } from '../deps/fromParamsToStringified'
import { cloneHeaders } from '../deps/toHeaders'

const fromStoreToParams = (store: OneRequestStore) => {
  // @TODO @james this needs some love, I hacked it to get it functional (from @michael)
  // store.has('tapParams')
  //   ? store.get('tapParams')(store.get('params') as ParamsObj)
  //   : store.get('params')

  if (store.has('params') === true) {
    const params = store.get('params')
    // console.log('fromStoreToParams - has params')
    return params

    /**
     * @throws @invalid
     * => { 'isStringied' => '[object Object]', 'two' => '2' } }
     * @example return new URLSearchParams(params)
     */
  } else {
    // console.warn('fromStoreToParams - has no params')
    return EMPTY_OBJ
  }
}

// @todo passing in string because serialize
// but could adapt here
const setParamsOnSearchParams = (params: string, urlObj: URL): void => {
  // @note - was just checking !params
  if (params !== '') {
    // Append params to existing params in the path
    const urlSearchParamEntries = new URLSearchParams(params)
    for (const [name, value] of urlSearchParamEntries) {
      if (!urlObj) {
        const stringifiedParams = JSON.stringify(params, undefined, 2)
        throw new TypeError(`
          did not pass url! ${stringifiedParams}
          url: ${urlObj}
        `)
      }
      if (urlObj.searchParams.has(name) === true) {
        console.warn('[1request] avoided duplicated param, sorry eh')
        console.log({ [name]: value }, '\n\n')
      } else {
        urlObj.searchParams.append(name, value)
      }
    }
  } else {
    // console.warn('no params - only warn if we have json type')
    // console.log({ tappedParams, params })
  }
}

/**
 * @note - this gets the urlObj from the store
 *         then appends serialized search params
 */
const setPostParams = (store: OneRequestStore, init: ScopedRequest): void => {
  const urlObj = store.get('urlObj')

  // @todo @@perf - we have UrlSearchParams returned from here
  const tappedParams = fromStoreToParams(store)
  const params = fromParamsToStringified(tappedParams)

  // debug
  // console.log('setPostParams', { tappedParams, params })

  setParamsOnSearchParams(params, urlObj)
  init.url = urlObj
}

function assignDefaultContentType(scoped: ScopedRequest): void {
  const contentType = scoped.headers.get('Content-Type') || false
  // cloned, immutable
  if (contentType === false) {
    scoped.headers.set('Content-Type', 'application/x-www-form-urlencoded')
  }
}

/**
 * @todo @name fetchAdapter
 * @description this started as postAdapter, now needs a bit to finish
 * ^ when we test POST, this should be good to go to finish
 */
function postAdapter(store: OneRequestStore): Request {
  const headers = cloneHeaders(store.get('headers'))

  const scoped: ScopedRequest = {
    url: store.get('urlObj'),
    headers,
    // @note
    // body: store.get('body'),
  }

  // @todo - this duplicates the content-type below...
  assignDefaultContentType(scoped)
  setPostParams(store, scoped)

  const init = {
    headers: scoped.headers,
    body: store.get('body'),
    credentials: store.get('credentials'),
    // @todo @validate
    method: store.get('method'),
  } as RequestInit

  serializeBody(init)

  // const url = decodeURIComponent('' + scoped.url)
  //   .replace(/\\"/g, '"')
  //   .replace(/\"/g, '"')
  const url = '' + scoped.url

  const request = new Request(url, init)
  return request
}

function isValidJSONBody(body: RequestBody) {
  return (
    body !== undefined &&
    typeof body !== 'string' &&
    !(body instanceof ArrayBuffer)
  )
}

/**
 * We accept arbitrary objects as body and serialize them as JSON
 */
function serializeBody(init: ScopedRequest) {
  const contentType = init.headers.get('Content-Type') || false
  if (contentType === 'json' && isValidJSONBody(init.body)) {
    init.body = JSON.stringify(init.body)
    // this may be a problem
    init.headers.set('Content-Type', 'application/json')
  }
  // @todo @@perf for this check
  else if (init.body) {
    init.body = encodeBody(init.body)
    config.get('logger').debug('`encoded` LOG REQ BODY', init.body)
    init.headers.set('Content-Type', 'application/x-www-form-urlencoded')
  }
  // default - get requests and others
  else {
    config.get('logger').debug('was not a POST request, or had no body')
  }
}

function adaptRequest(store: OneRequestStore): ResponseExtended<any> {
  const request = postAdapter(store)

  // @todo put it back from context for per-request settings...
  config
    .get('logger')
    .debug('[1request] doRequest (adaptRequest - pure fetch): ')
  const serialized = fromRequestToSerialized(request)
  config.get('logger').info(serialized)

  const requestWithDebug = request as any
  // fromRequestToSerialized.bind(request)
  requestWithDebug.toDebug = () => serialized

  return requestWithDebug
}

export {
  fromStoreToParams,
  setPostParams,
  postAdapter,
  serializeBody,
  adaptRequest,
  setParamsOnSearchParams,
}