Source: request_support.js

/**
 * requestSupport
 * @module requestSupport
 */
// @flow
const _ = require('lodash');
const yaml = require('js-yaml');
const handlebars = require('handlebars');
const requestPromise = require('request-promise');
const { parseStepArg } = require('./utilities');
const { get, set, unset,
  log, log3, initializeWith } = require('./universe').namespaceFactory('_cukelib');

const requestCommon = (routeStr, options) => {
  // combine options
  const requestOptions = _.defaults({}, options, get('_request.defaultOptions'));
  // construct URL
  const url = handlebars.compile(`${requestOptions.host}/${routeStr.replace(/^\//, '')}`)(get());
  requestOptions.url = url;
  delete requestOptions.host;
  // get cookie jar
  if (requestOptions.jar) {
    requestOptions.jar = get('_requestCookieJar') ||
      set('_requestCookieJar', requestPromise.jar());
  }
  // send request, log everything, capture response
  set('_requestOptions', requestOptions);
  log3('log3', 'requestOptions', requestOptions);
  const responsePromise = requestPromise(requestOptions);
  set('_requestResponsePromise', responsePromise);
  return responsePromise.then((result) => {
    log('response headers', result.headers);
    log('response body', result.body);
    unset('_requestResponsePromise');
    set('_requestResponse', result);
    return responsePromise;
  })
  .catch((err) => {
    log('err', err);
    return responsePromise;
  });
};

const parseYamlBody = (bodyStr) => {
  if (_.isPlainObject(bodyStr)) return bodyStr;
  if (!_.isString(bodyStr)) {
    throw new Error(`expected a string, but got ${bodyStr}`);
  }
  try {
    return yaml.safeLoad(bodyStr);
  } catch (err) {
    err.message += ' Error parsing:\n' + bodyStr; // eslint-disable-line prefer-template
    throw err;
  }
};


module.exports = {

  /**
   * Initializes the "request" defaults. Should be called in a context which
   * contains the CucumberJS methods (`Given`, `Then`, `Before`, etc.)
   *
   * @example
   * requestSupport.initialize.call(this, options);
   *
   * @param {object} [options={}] merged with
   *   Merged with [standard defaults](request_support.js.html#sunlight-1-line-67)
   *   to set request defaultOptions
   *
   * @returns undefined
   */
  initialize(options: Object = {}) {
    initializeWith.call(this, {
      _request: {
        defaultOptions: _.defaults({}, options, {
          host: 'http://localhost:3000',
          method: 'GET',
          simple: false,
          body: {},
          resolveWithFullResponse: true,
          json: true,
          jar: true,
        }),
      },
    });
  },


  /**
   * requestGET - Description
   *
   * @param {string}   routeStr     Description
   * @param {object} [options={}] Description
   *
   * @returns {type} Description
   */
  requestGET(routeStr: string, options: Object = {}) {
    return requestCommon(
      routeStr,
      _.assign({ method: 'GET' }, options)
    );
  },

  requestPUT(routeStr: string, bodyStr: string|Object, options: Object = {}) {
    const done = (typeof bodyStr === 'function') ? bodyStr : null;
    const responsePromise = requestCommon(
      routeStr,
      _.assign({ method: 'PUT', body: done ? {} : parseYamlBody(parseStepArg(bodyStr)) }, options)
    );
    if (done) {
      responsePromise.asCallback(done);
      return null;
    }
    return responsePromise;
  },

  requestDELETE(routeStr: string, options: Object = {}) {
    return requestCommon(
      routeStr,
      _.assign({ method: 'DELETE' }, options)
    );
  },

  /**
   * Executes POST request to given `routeStr`
   *
   * `bodyStr`
   *  - as a string will be interpreted as JSON and passed to the request.
   *  - as an object with a `raw` property it is interpreted as a single tow cucumber table.
   * The table contents are merged, interpreted as JSON and passed to the request.
   *  - as a plainObject it is passsed directly to the request.
   *  - as a function it is assumed to be a `done` callback and an empty body is sent to the request
   *
   * @param {string}   routeStr
   * @param {string|object|function}   bodyStr
   * @param {object} [options={}] Overides to the request defaults.
   *
   * @returns {Promise|null} Response promise from `request` or `null` for callback style calls.
   */
  requestPOST(routeStr: string, bodyStr: string|Object, options: Object = {}) {
    const done = (typeof bodyStr === 'function') ? bodyStr : null;
    const responsePromise = requestCommon(
      routeStr,
      _.assign({ method: 'POST', body: done ? {} : parseYamlBody(parseStepArg(bodyStr)) }, options)
    );
    if (done) {
      responsePromise.asCallback(done);
      return null;
    }
    return responsePromise;
  },
};