Show:
/**
  @module ember-flexberry-data
*/

/**
  Returns the type and boundary from the `Content-Type` header.

  @method getResponseMeta
  @param {String} contentTypeHeader The content of `Content-Type` header.
  @return {Object} Object with `contentType` and `boundary` properties.
*/
export function getResponseMeta(contentTypeHeader) {
  const [contentType, boundary] = contentTypeHeader.split(';');
  return { contentType, boundary: boundary ? boundary.split('=')[1] : null };
}

/**
  Returns an array of batch responses from the body of the HTTP response.

  @method getBatchResponses
  @param {String} response The body of the HTTP response.
  @param {String} boundary The boundary.
  @return {String[]} An array of batch responses.
*/
export function getBatchResponses(response, boundary) {
  const startBoundary = `--${boundary}`;
  const endBoundary = `--${boundary}--`;
  const responses = [];

  let lastResponse;
  response.split('\n').map(l => l.trim()).forEach((line) => {
    if (line === startBoundary || line === endBoundary) {
      if (lastResponse) {
        responses.push(lastResponse.join('\n'));
      }

      lastResponse = [];
    } else {
      lastResponse.push(line);
    }
  });

  return responses;
}

/**
  Parses a batch response depending on its type.

  The returned object always contains the `contentType` property.
  Depending on the type of response it may contain the following additional properties:
  - `response` - for responses with `application/http` content type.
  - `changesets` - for responses with `multipart/mixed` content type.

  @method parseBatchResponse
  @param {String} response The batch response.
  @return {Object} The object with the response description.
*/
export function parseBatchResponse(response) {
  const contentTypeHeader = getResponseHeader('Content-Type', response);
  const { contentType, boundary } = getResponseMeta(contentTypeHeader);
  switch (contentType) {
    case 'multipart/mixed': {
      const bodyStart = response.indexOf(`--${boundary}`);
      const changesets = getBatchResponses(response.substring(bodyStart), boundary).map(parseСhangeset);

      return { contentType, changesets };
    }
    case 'application/http':
      return { contentType, response: parseResponse(response) };

    default:
      throw new Error(`Unsupported type of response: ${contentType}.`);
  }
}

/**
  @private
  @method parseСhangeset
  @param {String} changeset The string with the changeset content.
  @return {Object} Object with `contentID`, `meta` and `body` properties.
*/
function parseСhangeset(changeset) {
  const contentID = getResponseHeader('Content-ID', changeset);
  const { meta, body } = parseResponse(changeset);

  return { contentID, meta, body };
}

/**
  @private
  @method parseResponse
  @param {String} response The string with the response content.
  @return {Object} Object with `meta` and `body` properties.
*/
function parseResponse(response) {
  const startMeta = response.indexOf('\n\n') + 1;
  const startBody = response.indexOf('\n\n', startMeta) + 1;

  const meta = parseResponseMeta(response.substring(startMeta, startBody));

  let body;
  switch (meta.contentType) {
    case null:
      body = null;
      break;

    case 'application/json':
      let parsedString = response.substring(startBody);
      body = JSON.parse(parsedString);

      /* There can be an error on server so result will be parsed but will not be applied and will lead to not understandable error.
        {"error":{"code":"500","message":"An error has occured."}}
      */
      if (parsedString.replace('\n', '').startsWith('{"error"')
          && body.hasOwnProperty('error')
          && body['error'].hasOwnProperty('code')
          && body['error'].hasOwnProperty('message')){
        let errorCode = body['error']['code'];
        let errorMessage = body['error']['message'];
        throw new Error(`Request failed, error ${errorCode}: ${errorMessage}`);
      }
      break;

    default:
      throw new Error(`Unsupported content type: ${meta.contentType}.`);
  }

  return { meta, body };
}

/**
  @private
  @method parseResponseMeta
  @param {String} rawMeta The string with the response metadata.
  @return {Object} Object with `status`, `statusText` and `contentType` properties.
*/
function parseResponseMeta(rawMeta) {
  const statusStart = rawMeta.indexOf(' ') + 1;
  const statusTextStart = rawMeta.indexOf(' ', statusStart) + 1;
  const end = rawMeta.indexOf('\n', statusTextStart);

  const status = parseInt(rawMeta.substring(statusStart, statusTextStart));
  const statusText = rawMeta.substring(statusTextStart, end === -1 ? rawMeta.length : end);

  const contentType = status !== 204 ? getResponseHeader('Content-Type', rawMeta).split(';')[0] : null;

  return { status, statusText, contentType };
}

/**
  @private
  @method getResponseHeader
  @param {String} header The header name.
  @param {String} response The response from which will be extracted header.
  @return {String} The content of the header.
*/
function getResponseHeader(header, response) {
  const fullHeader = `${header}: `;
  const start = response.indexOf(fullHeader) + fullHeader.length;
  const end = response.indexOf('\n', start);

  return response.substring(start, end === -1 ? response.length : end);
}