<commit_before>import * as LambdaProxy from './lambda-proxy';
import * as Joi from 'joi';
import * as _ from 'lodash';

import { ParameterDefinitionMap } from './parameter';

//
const DefaultJoiValidateOptions = {
  stripUnknown: true,
  presence: 'required',
  abortEarly: false,
};

// ---- RoutingContext
export class RoutingContext {
  private validatedParams: { [key:string]: any };
  private normalizedHeaders: { [key: string]: any };

  constructor(
    private request: LambdaProxy.Event,
    private pathParams: { [key:string]: string }
  ) {
    this.validatedParams = {};
    this.normalizedHeaders = null;
  }

  validateAndUpdateParams(parameterDefinitionMap: ParameterDefinitionMap) {
    const groupByIn: {
      [key: string]: { [key: string]: Joi.Schema }
    } = {};

    _.forEach(parameterDefinitionMap, (schema, name) => {
      if (!groupByIn[schema.in])
        groupByIn[schema.in] = {};

      groupByIn[schema.in][name] = schema.def;
    });

    // Path Params
    if (groupByIn['path']) {
      const res = Joi.validate(
        this.pathParams,
        Joi.object(groupByIn['path']),
        DefaultJoiValidateOptions,
      );
      if (res.error) {
        throw res.error;
      } else {
        Object.assign(this.validatedParams, res.value);
      }
    }

    // Query Params
    if (groupByIn['query']) {
      const res = Joi.validate(
        this.request.queryStringParameters || {},
        Joi.object(groupByIn['query']),
        DefaultJoiValidateOptions,
      );
      if (res.error) {
        throw res.error;
      } else {
        Object.assign(this.validatedParams, res.value);
      }
    }

    // Body Params
    if (groupByIn['body']) {
      const res = Joi.validate(
        this.bodyJSON,
        Joi.object(groupByIn['body']),
        DefaultJoiValidateOptions
      );
      if (res.error) {
        throw res.error;
      } else {
        Object.assign(this.validatedParams, res.value);
      }
    };
  }

  get headers(): LambdaProxy.EventHeaders {
    // normalize works lazily and should be cached for further use
    return this.normalizedHeaders
      || this.normalizeHeaders(this.request.headers);
  }

  get params() {
    return this.validatedParams;
  }

  // Body Parser
  get bodyJSON(): any {
    // TODO Check Header Content-Type
    return JSON.parse(this.request.body);
  }

  // Response Helpers
  json(json: any, statusCode?: number): LambdaProxy.Response {
    return {
      statusCode: statusCode || 200,
      headers: {
        'Content-Type': 'application/json; charset=utf-8'
      },
      body: JSON.stringify(json),
    };
  }

  // Helper for normalizing request headers
  private normalizeHeaders(headers: LambdaProxy.EventHeaders = {}) {
    return Object.keys(headers).reduce((hash, key) => {
      hash[key.toLowerCase()] = headers[key];

      return hash;
    }, Object.create(null));
  }
}<commit_msg>BACKEND-328: add ability to insert response headers<commit_after>import * as LambdaProxy from './lambda-proxy';
import * as Joi from 'joi';
import * as _ from 'lodash';

import { ParameterDefinitionMap } from './parameter';

//
const DefaultJoiValidateOptions = {
  stripUnknown: true,
  presence: 'required',
  abortEarly: false,
};

// ---- RoutingContext
export class RoutingContext {
  private validatedParams: { [key:string]: any };
  private normalizedHeaders: { [key: string]: any };

  constructor(
    private request: LambdaProxy.Event,
    private pathParams: { [key:string]: string }
  ) {
    this.validatedParams = {};
    this.normalizedHeaders = null;
  }

  validateAndUpdateParams(parameterDefinitionMap: ParameterDefinitionMap) {
    const groupByIn: {
      [key: string]: { [key: string]: Joi.Schema }
    } = {};

    _.forEach(parameterDefinitionMap, (schema, name) => {
      if (!groupByIn[schema.in])
        groupByIn[schema.in] = {};

      groupByIn[schema.in][name] = schema.def;
    });

    // Path Params
    if (groupByIn['path']) {
      const res = Joi.validate(
        this.pathParams,
        Joi.object(groupByIn['path']),
        DefaultJoiValidateOptions,
      );
      if (res.error) {
        throw res.error;
      } else {
        Object.assign(this.validatedParams, res.value);
      }
    }

    // Query Params
    if (groupByIn['query']) {
      const res = Joi.validate(
        this.request.queryStringParameters || {},
        Joi.object(groupByIn['query']),
        DefaultJoiValidateOptions,
      );
      if (res.error) {
        throw res.error;
      } else {
        Object.assign(this.validatedParams, res.value);
      }
    }

    // Body Params
    if (groupByIn['body']) {
      const res = Joi.validate(
        this.bodyJSON,
        Joi.object(groupByIn['body']),
        DefaultJoiValidateOptions
      );
      if (res.error) {
        throw res.error;
      } else {
        Object.assign(this.validatedParams, res.value);
      }
    };
  }

  get headers(): LambdaProxy.EventHeaders {
    // normalize works lazily and should be cached for further use
    return this.normalizedHeaders
      || this.normalizeHeaders(this.request.headers);
  }

  get params() {
    return this.validatedParams;
  }

  // Body Parser
  get bodyJSON(): any {
    // TODO Check Header Content-Type
    return JSON.parse(this.request.body);
  }

  // Response Helpers
  json(
    json: any,
    statusCode: number = 200,
    headers: LambdaProxy.EventHeaders = {}
  ): LambdaProxy.Response {
    return {
      statusCode: statusCode,
      headers: {
        'Content-Type': 'application/json; charset=utf-8',
        ...headers,
      },
      body: JSON.stringify(json),
    };
  }

  // Helper for normalizing request headers
  private normalizeHeaders(headers: LambdaProxy.EventHeaders = {}) {
    return Object.keys(headers).reduce((hash, key) => {
      hash[key.toLowerCase()] = headers[key];

      return hash;
    }, Object.create(null));
  }
}
{'fraction_non_alphanumeric': 0.13327688399661303, 'fraction_numerical': 0.0018628281117696868, 'mean_word_length': 2.547147147147147, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '1513811', 'n_tokens_mistral': 1825, 'n_tokens_neox': 1795, 'n_words': 374}