<commit_before>const jph = require('json-parse-helpfulerror');
const mediaTyper = require('media-typer');
const contentTypeUtils = require('content-type');

const { TextDiff } = require('../../../lib/validators/text-diff');
const { JsonExample } = require('../../../lib/validators/json-example');
const { JsonSchema } = require('../../../lib/validators/json-schema');

function isPlainText(mediaType) {
  return mediaType.type === 'text' && mediaType.subtype === 'plain';
}

function isJson(mediaType) {
  return (
    (mediaType.type === 'application' && mediaType.subtype === 'json') ||
    mediaType.suffix === 'json'
  );
}

function isJsonSchema(mediaType) {
  return (
    mediaType.type === 'application' &&
    mediaType.subtype === 'schema' &&
    mediaType.suffix === 'json'
  );
}

/**
 * Parses a given content-type header into media type.
 * @param {string} contentType
 * @returns {[Error, MediaType]}
 */
function parseContentType(contentType) {
  try {
    const { type } = contentTypeUtils.parse(`${contentType}`);
    const mediaType = mediaTyper.parse(type);
    return [null, mediaType];
  } catch (error) {
    return [error, null];
  }
}

/**
 * Determines if a given 'Content-Type' header contains JSON.
 * @param {string} contentType
 * @returns {boolean}
 */
function isJsonContentType(contentType) {
  // No need to explicitly check for "contentType" existence.
  // In case given content type is invalid, "parseContentType"
  // will return an error, which would be coerced to "false".
  const [error, mediaType] = parseContentType(contentType);

  // Silence an error on purporse because contentType
  // may contain any kind of rubbish.
  return error ? false : isJson(mediaType);
}

/**
 * Returns a tuple of error and body media type based
 * on the given body and normalized headers.
 * @param {string} body
 * @param {Object} headers
 * @returns {[error, bodyType]}
 */
function getBodyType(body, contentType) {
  const hasJsonContentType = isJsonContentType(contentType);

  try {
    jph.parse(body);
    const [mediaTypeError, bodyMediaType] = parseContentType(
      hasJsonContentType ? contentType : 'application/json'
    );

    if (mediaTypeError) {
      throw new Error(mediaTypeError);
    }

    return [null, bodyMediaType];
  } catch (parsingError) {
    const fallbackMediaType = mediaTyper.parse('text/plain');
    const error = hasJsonContentType
      ? /**
         * @TODO The same message for real/expected
         * body assertion. Real/expected must be reflected
         * in the error message.
         */
        `\
Real body 'Content-Type' header is '${contentType}' \
but body is not a parseable JSON:
${parsingError.message}\
        `
      : null;

    return [error, fallbackMediaType];
  }
}

/**
 * Returns a tuple of error and schema media type
 * based on given body schema.
 * @param {string} bodySchema
 * @returns {[error, schemaType]}
 */
function getBodySchemaType(bodySchema) {
  const jsonSchemaType = mediaTyper.parse('application/schema+json');

  if (typeof bodySchema !== 'string') {
    return [null, jsonSchemaType];
  }

  try {
    const parsed = jph.parse(bodySchema);
    if (typeof parsed === 'object') {
      return [null, jsonSchemaType];
    }
  } catch (exception) {
    const error = `Can't validate. Expected body JSON Schema is not a parseable JSON:\n${
      exception.message
    }`;

    return [error, null];
  }
}

/**
 * Returns a body validator class based on the given
 * real and expected body media types.
 * @param {MediaType} realType
 * @param {MediaType} expectedType
 * @returns {Validator}
 */
function getBodyValidator(realType, expectedType) {
  const both = (predicate) => (real, expected) => {
    return [real, expected].every(predicate);
  };

  const validators = [
    [TextDiff, both(isPlainText)],
    // List JsonSchema first, because weak predicate of JsonExample
    // would resolve on "application/schema+json" media type too.
    [
      JsonSchema,
      (real, expected) => {
        return isJson(real) && isJsonSchema(expected);
      }
    ],
    [JsonExample, both(isJson)]
  ];

  const validator = validators.find(([_name, predicate]) => {
    return predicate(realType, expectedType);
  });

  if (!validator) {
    const error = `Can't validate real media type '${mediaTyper.format(
      realType
    )}' against expected media type '${mediaTyper.format(expectedType)}'.`;

    // const error = {
    //   message:
    //   severity: 'error'
    // };
    return [error, null];
  }

  return [null, validator[0]];
}

/**
 * Validates given bodies of transaction elements.
 * @param {Object} real
 * @param {Object} expected
 */
function validateBody(real, expected) {
  const results = [];<commit_msg>refactor: removes content-type parsing error handling from "getBodyType"<commit_after>const jph = require('json-parse-helpfulerror');
const mediaTyper = require('media-typer');
const contentTypeUtils = require('content-type');

const { TextDiff } = require('../../../lib/validators/text-diff');
const { JsonExample } = require('../../../lib/validators/json-example');
const { JsonSchema } = require('../../../lib/validators/json-schema');

function isPlainText(mediaType) {
  return mediaType.type === 'text' && mediaType.subtype === 'plain';
}

function isJson(mediaType) {
  return (
    (mediaType.type === 'application' && mediaType.subtype === 'json') ||
    mediaType.suffix === 'json'
  );
}

function isJsonSchema(mediaType) {
  return (
    mediaType.type === 'application' &&
    mediaType.subtype === 'schema' &&
    mediaType.suffix === 'json'
  );
}

/**
 * Parses a given content-type header into media type.
 * @param {string} contentType
 * @returns {[Error, MediaType]}
 */
function parseContentType(contentType) {
  try {
    const { type } = contentTypeUtils.parse(`${contentType}`);
    return mediaTyper.parse(type);
  } catch (error) {
    return null;
  }
}

/**
 * Determines if a given 'Content-Type' header contains JSON.
 * @param {string} contentType
 * @returns {boolean}
 */
function isJsonContentType(contentType) {
  try {
    const mediaType = parseContentType(contentType);
    return isJson(mediaType);
  } catch (error) {
    // Silence an error on purporse because contentType
    // may contain any kind of rubbish.
    return false;
  }
}

/**
 * Returns a tuple of error and body media type based
 * on the given body and normalized headers.
 * @param {string} body
 * @param {Object} headers
 * @returns {[error, bodyType]}
 */
function getBodyType(body, contentType) {
  const hasJsonContentType = isJsonContentType(contentType);

  try {
    jph.parse(body);
    const bodyMediaType = parseContentType(
      hasJsonContentType ? contentType : 'application/json'
    );

    return [null, bodyMediaType];
  } catch (parsingError) {
    const fallbackMediaType = mediaTyper.parse('text/plain');
    const error = hasJsonContentType
      ? /**
         * @TODO The same message for real/expected
         * body assertion. Real/expected must be reflected
         * in the error message.
         */
        `\
Real body 'Content-Type' header is '${contentType}' \
but body is not a parseable JSON:
${parsingError.message}\
        `
      : null;

    return [error, fallbackMediaType];
  }
}

/**
 * Returns a tuple of error and schema media type
 * based on given body schema.
 * @param {string} bodySchema
 * @returns {[error, schemaType]}
 */
function getBodySchemaType(bodySchema) {
  const jsonSchemaType = mediaTyper.parse('application/schema+json');

  if (typeof bodySchema !== 'string') {
    return [null, jsonSchemaType];
  }

  try {
    const parsed = jph.parse(bodySchema);
    if (typeof parsed === 'object') {
      return [null, jsonSchemaType];
    }
  } catch (exception) {
    const error = `Can't validate. Expected body JSON Schema is not a parseable JSON:\n${
      exception.message
    }`;

    return [error, null];
  }
}

/**
 * Returns a body validator class based on the given
 * real and expected body media types.
 * @param {MediaType} realType
 * @param {MediaType} expectedType
 * @returns {Validator}
 */
function getBodyValidator(realType, expectedType) {
  const both = (predicate) => (real, expected) => {
    return [real, expected].every(predicate);
  };

  const validators = [
    [TextDiff, both(isPlainText)],
    // List JsonSchema first, because weak predicate of JsonExample
    // would resolve on "application/schema+json" media type too.
    [
      JsonSchema,
      (real, expected) => {
        return isJson(real) && isJsonSchema(expected);
      }
    ],
    [JsonExample, both(isJson)]
  ];

  const validator = validators.find(([_name, predicate]) => {
    return predicate(realType, expectedType);
  });

  if (!validator) {
    const error = `Can't validate real media type '${mediaTyper.format(
      realType
    )}' against expected media type '${mediaTyper.format(expectedType)}'.`;
    return [error, null];
  }

  return [null, validator[0]];
}

/**
 * Validates given bodies of transaction elements.
 * @param {Object} real
 * @param {Object} expected
 */
function validateBody(real, expected) {
  const results = [];
{'fraction_non_alphanumeric': 0.13744954728918948, 'fraction_numerical': 0.00021817388458601506, 'mean_word_length': 3.3553444180522565, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2108716', 'n_tokens_mistral': 2752, 'n_tokens_neox': 2704, 'n_words': 769}