Source: mHelpDesk/createCreateMovementRequestFunction.js

/**
 * @module mHelpDesk/craeteCreateMovementRequest
 */

const axios = require('axios')

const { getPromiseOfAccessToken } = require('./authentication')
const config = require('../config')

/**
 * Create a movement request handler function. This is used to generate the
 * handlers for pickup and delivery requests, because they are so similar.
 * @param {string} typeId - The string of the job typeId to give mHelpDesk.
 * @return {Function} A function to handle movement requests of the given type.
 */
exports.createCreateMovementRequestFunction = (
  typeId, customFieldXMLNames, attachEquipment
) => async xmlObject => {
  const accessToken = await getPromiseOfAccessToken()

  // TODO: Remove this line once we can use service locations.
  // eslint-disable-next-line no-unused-vars
  const [serviceLocationData, equipmentData] = await Promise.all([
    makeAPIRequest(
      'get',
      `/customers/${config.api.fieldValues.customerId}/servicelocations`,
      accessToken
    ),
    attachEquipment
      ? makeAPIRequest('get', '/equipment', accessToken) : Promise.resolve()
  ])

  // TODO: Find the ids of the service location for this job.
  // At the moment we cannot get service location custom fields, and so
  // cannot find which ones are to be associated with the job, this will
  // have to be done manually. This will need to change later.
  const serviceLocation = { id: config.api.fieldValues.customerId }

  let equipment
  if (attachEquipment) {
    const matchingEquipment = equipmentData.results
      .map(equipment => ({
        id: equipment.equipmentId,
        clientReferenceNumber: equipment.customFields.filter(
          ({ customFieldId }) => customFieldId ===
          config.api.jobCustomFieldMappings.ClientReferenceNumber
        )[0].value
      }))
      .filter(
        ({ clientReferenceNumber }) =>
          clientReferenceNumber === ('' + xmlObject.ClientReferenceNumber)
      )
    if (matchingEquipment.length !== 1) {
      throw new Error(
        `Found ${matchingEquipment.length} matching equipment, expected 1`
      )
    }
    equipment = matchingEquipment[0]
  }

  const priority = {
    'Standard': '1',
    'Normal': '1',
    'High': '2',
    'Urgent': '3',
    'Critical': '4'
  }[xmlObject['Priority']]
  if (!priority) {
    throw new Error(`Invalid priority: ${xmlObject['Priority']}`)
  }

  // Find and build the custom field values.
  const valueTranslators = {
    'Hinging': x => x[0].toUpperCase(),
    'IsLiftingRequired': x => x.toLowerCase() === 'true' ? 'True' : 'False',
    'IsLightBoxRequired': x => x.toLowerCase() === 'true' ? 'y' : 'n',
    'IsLockRequired': x => x.toLowerCase() === 'true' ? 'y' : 'n'
  }
  const customFields = customFieldXMLNames
    .map(customField => {
      const translator = valueTranslators[customField] || (value => value)
      return {
        customFieldId: config.api.jobCustomFieldMappings[customField],
        fieldValue: translator(xmlObject[customField][0])
      }
    })
  if (attachEquipment) {
    customFields.push({
      customFieldId: config.api.nonXMLCustomFieldIds.CRN_Equipment_Number,
      fieldValue: equipment.clientReferenceNumber
    })
  }

  const job = {
    'customerId': config.api.fieldValues.customerId,
    'serviceLocationId': serviceLocation.id,
    'subject': 'Awaiting Job Name',
    'statusId': 1,
    priority,
    'summary': '',
    typeId,
    'customFields': customFields,
    'equipment': attachEquipment ? [equipment.id] : []
  }

  // Create the job on mHelpDesk.
  const jobCreationResponseData = await makeAPIRequest(
    'post', '/tickets', accessToken, job
  )
  // Get the ticket name, update it on mHelpDesk, and return it.
  job.subject = `PA-${jobCreationResponseData.ticketNumber}M`
  await makeAPIRequest(
    'put', `/tickets/${jobCreationResponseData.ticketId}`, accessToken, job
  )
  return jobCreationResponseData.ticketNumber
}

/**
 * Make a request of the given form, using the given access token, to the
 * mHelpDesk API. It throws a custom error message if the request fails,
 * returns any data yeilded by the response.
 * @param {string} method - The http method to use.
 * @param {string} url - The sub URL of the API to use.
 * @param {string} accessToken - The acces token to authenticate with.
 * @param {string} data - Any data to include.
 * @returns {object} The data of the response.
 */
const makeAPIRequest = (method, url, accessToken, data) => {
  const axiosConfig = {
    method,
    baseURL: config.api.url + `/portal/${config.api.portalId}`,
    url,
    headers: { 'Authorization': 'bearer ' + accessToken }
  }
  if (data) axiosConfig.data = data
  return axios(axiosConfig)
    .then(response => response.data)
    .catch(error => {
      error.message = 'mHelpDesk API usage failed. Status ' +
        `${error.response.status} ${error.response.statusText}`
      throw error
    })
}