<commit_before>'use strict'
const Joi = require('joi')
const find = require('lodash/find')
const defaults = require('lodash/defaults')
const ValidationError = require('./validation-error')

const defaultOptions = {
  allowUnknownHeaders: true,
  allowUnknownBody: true,
  allowUnknownQuery: true,
  allowUnknownParams: true,
  allowUnknownCookies: true,
  flatten: false,
  mutateRequest: true,
  status: 400,
  statusText: 'Bad Request',
}
let globalOptions = {}

// maps the corresponding request object to an `express-validation` option
const unknownMap = {
  headers: 'allowUnknownHeaders',
  body: 'allowUnknownBody',
  query: 'allowUnknownQuery',
  params: 'allowUnknownParams',
  cookies: 'allowUnknownCookies',
}
const locations = ['body', 'cookies', 'headers', 'params', 'query']

exports = module.exports = function validation (schema) {
  if (!schema) throw new Error('Please provide a validation schema')

  return function validationMiddleware (req, res, next) {
    const errors = []

    // Set default options
    const options = defaults({}, schema.options || {}, globalOptions, defaultOptions)
    const { status, statusText, flatten } = options

    for (let i = 0; i < locations.length; i++) {
      const location = locations[i]

      if (schema[location]) {
        // prepares parameters for validate
        const allowUnknown = options[unknownMap[location]]
        const { mutateRequest } = options
        // extracts only required parts from request
        const { body, cookies, headers, params, query } = req
        const context = { body, cookies, headers, params, query }

        validate(errors, req, schema, location, allowUnknown, context, mutateRequest)
      }
    }

    if (errors.length === 0) return next()
    return next(new ValidationError(errors, status, statusText, flatten))
  }
}

exports.ValidationError = ValidationError

exports.options = function (opts) {
  if (!opts) {
    globalOptions = {}
    return
  }

  globalOptions = defaults({}, globalOptions, opts)
}

/**
 * validate checks the current `Request` for validations
 * NOTE: mutates `request` in case the object is valid.
 */
function validate (errObj, request, schema, location, allowUnknown, context, mutateRequest) {
  if (!request || !schema) return

  const joiOptions = { context, allowUnknown, abortEarly: false }

  const { error, value } = Joi.validate(request[location], schema[location], joiOptions)
  if (!error || error.details.length === 0) {
    if (mutateRequest) request[location] = value
    return
  }
  error.details.forEach(function (error) {
    const errorExists = find(errObj, function (item) {
      if (item && item.field === error.path && item.location === location) {
        item.messages.push(error.message)
        item.types.push(error.type)
        return item
      }
      return
    })

    if (!errorExists) {
      errObj.push({
        field: error.path,
        location: location,
        messages: [error.message],
        types: [error.type],
      })
    }
  })
};
<commit_msg>adding stripUnknown and breaking change on allowUnknown option syntax<commit_after>'use strict'
const Joi = require('joi')
const find = require('lodash/find')
const defaults = require('lodash/defaults')
const ValidationError = require('./validation-error')

const defaultOptions = {
  allowUnknown: true,
  stripUnknown: false,
  flatten: false,
  mutateRequest: true,
  status: 400,
  statusText: 'Bad Request',
}
let globalOptions = {}

const locations = ['body', 'cookies', 'headers', 'params', 'query']

// hasOptions keeps `options` sane
function hasOption (options, property, lookingFor) {
  if (!options) return false
  if (typeof options[property] === 'boolean') return options[property]
  if (
    typeof options === 'object' &&
    typeof options[property] === 'object' &&
    typeof options[property][lookingFor] === 'boolean'
  ) {
    return options[property][lookingFor]
  }
  return false
}

// validation returns an express.js middleware starting from a Joi schema
function validation (schema) {
  if (!schema) throw new Error('Please provide a validation schema')

  // Set default options
  const options = defaults({}, schema.options || {}, globalOptions, defaultOptions)
  const { status, statusText, flatten } = options

  return function validationMiddleware (req, res, next) {
    const errors = []

    for (let i = 0; i < locations.length; i++) {
      const location = locations[i]

      if (schema[location]) {
        const { mutateRequest } = options
        // extracts only required parts from request
        const { body, cookies, headers, params, query } = req
        const context = { body, cookies, headers, params, query }
        // retrieves Joi parameters
        const allowUnknown = hasOption(options, 'allowUnknown', location)
        const stripUnknown = hasOption(options, 'stripUnknown', location)

        const joiOptions = { abortEarly: false, allowUnknown, context, stripUnknown }

        validateProperty(errors, req, schema, location, mutateRequest, joiOptions)
      }
    }

    if (errors.length === 0) return next()
    return next(new ValidationError(errors, status, statusText, flatten))
  }
}

function setOptions (opts) {
  if (!opts) {
    globalOptions = {}
    return
  }

  globalOptions = defaults({}, globalOptions, opts)
}

/**
 * validate checks the current `Request` for validations
 * NOTE: mutates `request` in case the object is valid.
 */
function validateProperty (errObj, request, schema, location, mutateRequest, joiOptions) {
  if (!request || !schema) return

  const { error, value } = Joi.validate(request[location], schema[location], joiOptions)
  if (!error || error.details.length === 0) {
    if (mutateRequest) request[location] = value
    return
  }
  error.details.forEach(function (error) {
    const errorExists = find(errObj, function (item) {
      if (item && item.field === error.path && item.location === location) {
        item.messages.push(error.message)
        item.types.push(error.type)
        return item
      }
      return
    })

    if (!errorExists) {
      errObj.push({
        field: error.path,
        location: location,
        messages: [error.message],
        types: [error.type],
      })
    }
  })
};

exports = module.exports = validation
exports.options = setOptions
exports.ValidationError = ValidationError

{'fraction_non_alphanumeric': 0.12141176470588236, 'fraction_numerical': 0.0018823529411764706, 'mean_word_length': 3.46186144156753, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, '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': '5659285', 'n_tokens_mistral': 1795, 'n_tokens_neox': 1746, 'n_words': 538}