'use strict';

/**
 * Parses and serializes HTTP headers, used as parameters, i.e. namespaced
 */

const { titleize, dasherize } = require('underscore.string');
const { mapKeys } = require('lodash');
const parsePreferHeader = require('parse-prefer-header');

const { EngineError } = require('../../error');


/**
 * Returns a request's or response's HTTP headers
 * Only returns the namespaced ones, e.g. X-MYNAMESPACE-HEADER,
 * and remove their namespace
 * Also returns the headers that provide application data,
 * not just HTTP protocol semantics, according to a whitelist.
 *
 * @param {req|res} reqOrRes - HTTP request or response object
 * @param {string} projectName - MYNAMESPACE
 * @returns {object} appHeaders
 */
const parse = function (reqOrRes, projectName) {
  const headers = getHeaders(reqOrRes);
  const nonAppHeaders = getNonAppHeaders(headers, projectName);
  const appHeaders = getAppHeaders(headers, projectName);

  return { nonAppHeaders, appHeaders };
};

// Returns request or response headers
// Note that node.js automatically converts header names to lowercase
const getHeaders = function (reqOrRes) {
  // Is a response
  if (reqOrRes.getHeaders) {
    return reqOrRes.getHeaders();
  // Is a request
  } else {
    return reqOrRes.headers || {};
  }
};

// Filters headers with only the headers whose name starts with X-NAMESPACE-
const getAppHeaders = function (headers, projectName) {
  const headersNamespace = `x-${dasherize(projectName)}-`;
  // We don't need to RegExp-escape since `headersNamespace` only contains
  // ASCII letters, numbers and dashes
  const appHeaderRegex = new RegExp(`^${headersNamespace}`);
  return Object.entries(headers)
    .filter(([name]) => appHeaderRegex.test(name))
    .map(([name, value]) => {
      const shortName = name.replace(appHeaderRegex, '');
      return { [shortName]: value };
    })
    .reduce((memo, obj) => Object.assign(memo, obj), {});
};

// Inverse
const getNonAppHeaders = function (headers, projectName) {
  const headersNamespace = `x-${dasherize(projectName)}-`;
  const appHeaderRegex = new RegExp(`^${headersNamespace}`);
  return Object.entries(headers)
    .filter(([name]) => !appHeaderRegex.test(name))
    .map(([name, value]) => ({ [name]: value }))
    .reduce((memo, obj) => Object.assign(memo, obj), {});
};

/**
 * Take a plain object of parameters, and transforms it in an object that
 * can be set as headers to a HTTP request or response
 * Namespaces headers names, e.g. X-Mynamespace-Header,
 * unless part of a whitelist
 *
 * @param {object} appHeaders
 * @param {string} projectName - MYNAMESPACE
 * @returns {object} headers
 */
const serializeAppHeaders = function (headers, projectName) {
  const headersNamespace = `x-${dasherize(projectName)}-`;
  return mapKeys(headers, (_, headerName) => {
    return titleize(`${headersNamespace}${headerName}`);
  });
};

// Parses Prefer HTTP header
const parsePrefer = function ({ headers: { prefer } }) {
  if (!prefer) { return {}; }

  try {
    return parsePreferHeader(prefer);
  } catch (error) {
    const message = `HTTP 'Prefer' header value syntax error: ${prefer}`;
    throw new EngineError(message, { reason: 'INPUT_VALIDATION' });
  }
};


module.exports = {
  httpHeaders: {
    parse,
    parsePrefer,
    serialize: serializeAppHeaders,
  },
};

@@ -2314,573 +2314,8 @@
 %7D;%0A%0A
-/**%0A * Take a plain object of parameters, and transforms it in an object that%0A * can be set as headers to a HTTP request or response%0A * Namespaces headers names, e.g. X-Mynamespace-Header,%0A * unless part of a whitelist%0A *%0A * @param %7Bobject%7D appHeaders%0A * @param %7Bstring%7D projectName - MYNAMESPACE%0A * @returns %7Bobject%7D headers%0A */%0Aconst serializeAppHeaders = function (headers, projectName) %7B%0A  const headersNamespace = %60x-$%7Bdasherize(projectName)%7D-%60;%0A  return mapKeys(headers, (_, headerName) =%3E %7B%0A    return titleize(%60$%7BheadersNamespace%7D$%7BheaderName%7D%60);%0A  %7D);%0A%7D;%0A%0A
 // P
@@ -2339,16 +2339,16 @@
  header%0A
+
 const pa
@@ -2643,16 +2643,226 @@
   %7D%0A%7D;%0A%0A
+// Set HTTP header, ready to be sent with response%0Aconst send = function (%7B specific: %7B res %7D, headers = %7B%7D %7D) %7B%0A  for (const %5Bname, value%5D of Object.entries(headers)) %7B%0A    res.setHeader(name, value);%0A  %7D%0A%7D;%0A%0A
 %0Amodule.
@@ -2928,36 +2928,10 @@
   se
-rialize: serializeAppHeaders
+nd
 ,%0A  

Add way to set headers