<commit_before>import * as ware from 'ware';
import { IncomingMessage, ServerResponse } from 'http';
import { pluralize } from 'inflection';
import { fromNode } from 'bluebird';
import * as typeis from 'type-is';
import * as createDebug from 'debug';
import Errors from './errors';
import Route from './route';
import Request, { Method } from './request';
import ensureArray = require('arrify');
import DenaliObject from '../metal/object';
import Logger from './logger';
import Container from './container';
import Action from './action';
import Serializer from '../data/serializer';
import {
  find,
  forEach
 } from 'lodash';

const debug = createDebug('denali:router');

interface RoutesCache {
  get: Route[];
  post: Route[];
  put: Route[];
  patch: Route[];
  delete: Route[];
  head: Route[];
  options: Route[];
  [method: string]: Route[];
};

interface MiddlewareFn {
  (req: IncomingMessage, res: ServerResponse, next: Function): void;
}

interface ResourceOptions {
  /**
   * Should routes for related resources be generated? If true, routes will be generated following
   * the JSON-API recommendations for relationship URLs.
   *
   * @see {@link http://jsonapi.org/recommendations/#urls-relationships|JSON-API URL
   * Recommendatiosn}
   */
  related?: boolean;
  /**
   * A list of action types to _not_ generate.
   */
  except?: string[];
  /**
   * A list of action types that should be the _only_ ones generated.
   */
  only?: string[];
}

interface RouterDSL {
  get(pattern: string, action: string, params: {}): void;
  post(pattern: string, action: string, params: {}): void;
  put(pattern: string, action: string, params: {}): void;
  patch(pattern: string, action: string, params: {}): void;
  delete(pattern: string, action: string, params: {}): void;
  head(pattern: string, action: string, params: {}): void;
  options(pattern: string, action: string, params: {}): void;
  resource(resourceName: string, options: ResourceOptions): void;
}

/**
 * The Router handles incoming requests, sending them to the appropriate action. It's also
 * responsible for defining routes in the first place - it's passed into the `config/routes.js`
 * file's exported function as the first argument.
 *
 * @module denali
 * @submodule runtime
 */
export default class Router extends DenaliObject implements RouterDSL {

  /**
   * The cache of available routes.
   */
  public routes: RoutesCache = {
    get: [],
    post: [],
    put: [],
    patch: [],
    delete: [],
    head: [],
    options: []
  };

  /**
   * The internal generic middleware handler, responsible for building and executing the middleware
   * chain.
   */
  private middleware: any = (<() => any>ware)();

  /**
   * The application logger instance
   */
  private logger: Logger;

  constructor(options: { container: Container, logger: Logger }) {
    super();
    this.container = options.container;
    this.logger = options.logger;
  }

  /**
   * Helper method to invoke the function exported by `config/routes.js` in the context of the
   * current router instance.
   */
  public map(fn: (router: Router) => void): void {
    debug('mapping routes');
    fn(this);
  }

  /**
   * Takes an incoming request and it's response from an HTTP server, prepares them, runs the
   * generic middleware first, hands them off to the appropriate action given the incoming URL, and
   * finally renders the response.
   */
  public async handle(req: IncomingMessage, res: ServerResponse): Promise<void> {
    let request = new Request(req);
    try {

      debug(`[${ request.id }]: ${ request.method.toUpperCase() } ${ request.path }`);
      await fromNode((cb) => this.middleware.run(req, res, cb));

      debug(`[${ request.id }]: routing request`);
      let routes = this.routes[request.method];
      for (let i = 0; i < routes.length; i += 1) {
        request.params = routes[i].match(request.path);
        if (request.params) {
          request.route = routes[i];
          break;
        }
      }
      if (!request.route) {
        debug(`[${ request.id }]: ${ request.method } ${ request.path } did match any route. Available ${ request.method } routes:\n${ routes.map((r) => r.spec).join(',\n') }`);
        throw new Errors.NotFound('Route not recognized');
      }

      // eslint-disable-next-line new-cap
      let action: Action = new (<any>request.route.action)({
        request,
        logger: this.logger,
        container: this.container
      });

      let serializer: Serializer | false;
      if (action.serializer != null) {
        if (typeof action.serializer === 'string') {
          serializer = this.container.lookup(`serializer:${ action.serializer }`);
        } else if (action.serializer === false) {
          serializer = null;
        } else {
          serializer = <Serializer | false>action.serializer;
        }
      } else {
        serializer = this.container.lookup('serializer:application');
      }

      if (typeis.hasBody(request) && serializer) {
        debug(`[${ request.id }]: parsing request body`);
        request.body = serializer.parse(request.body);
      }

      debug(`[${ request.id }]: running action`);
      let response = await action.run();

      debug(`[${ request.id }]: setting response status code to ${ response.status }`);
      res.statusCode = response.status;
      res.setHeader('X-Request-Id', request.id);

      if (response.body) {
        debug(`[${ request.id }]: writing response body`);
        res.setHeader('Content-type', response.contentType);
        if (this.config.environment !== 'production') {
          res.write(JSON.stringify(response.body, null, 2));
        } else {
          res.write(JSON.stringify(response.body));
        }
      }

      res.end();
      debug(`[${ request.id }]: response finished`);

    } catch (error) {
      return this.handleError(request, res, error);
    }
  }

  /**
   * Takes a request, response, and an error and hands off to the generic application level error<commit_msg>fix(runtime): fix config reference on router<commit_after>import * as ware from 'ware';
import { IncomingMessage, ServerResponse } from 'http';
import { pluralize } from 'inflection';
import { fromNode } from 'bluebird';
import * as typeis from 'type-is';
import * as createDebug from 'debug';
import Errors from './errors';
import Route from './route';
import Request, { Method } from './request';
import ensureArray = require('arrify');
import DenaliObject from '../metal/object';
import Logger from './logger';
import Container from './container';
import Action from './action';
import Serializer from '../data/serializer';
import {
  find,
  forEach
 } from 'lodash';

const debug = createDebug('denali:router');

interface RoutesCache {
  get: Route[];
  post: Route[];
  put: Route[];
  patch: Route[];
  delete: Route[];
  head: Route[];
  options: Route[];
  [method: string]: Route[];
};

interface MiddlewareFn {
  (req: IncomingMessage, res: ServerResponse, next: Function): void;
}

interface ResourceOptions {
  /**
   * Should routes for related resources be generated? If true, routes will be generated following
   * the JSON-API recommendations for relationship URLs.
   *
   * @see {@link http://jsonapi.org/recommendations/#urls-relationships|JSON-API URL
   * Recommendatiosn}
   */
  related?: boolean;
  /**
   * A list of action types to _not_ generate.
   */
  except?: string[];
  /**
   * A list of action types that should be the _only_ ones generated.
   */
  only?: string[];
}

interface RouterDSL {
  get(pattern: string, action: string, params: {}): void;
  post(pattern: string, action: string, params: {}): void;
  put(pattern: string, action: string, params: {}): void;
  patch(pattern: string, action: string, params: {}): void;
  delete(pattern: string, action: string, params: {}): void;
  head(pattern: string, action: string, params: {}): void;
  options(pattern: string, action: string, params: {}): void;
  resource(resourceName: string, options: ResourceOptions): void;
}

/**
 * The Router handles incoming requests, sending them to the appropriate action. It's also
 * responsible for defining routes in the first place - it's passed into the `config/routes.js`
 * file's exported function as the first argument.
 *
 * @module denali
 * @submodule runtime
 */
export default class Router extends DenaliObject implements RouterDSL {

  /**
   * The cache of available routes.
   */
  public routes: RoutesCache = {
    get: [],
    post: [],
    put: [],
    patch: [],
    delete: [],
    head: [],
    options: []
  };

  /**
   * The internal generic middleware handler, responsible for building and executing the middleware
   * chain.
   */
  private middleware: any = (<() => any>ware)();

  /**
   * The application logger instance
   */
  private logger: Logger;

  constructor(options: { container: Container, logger: Logger }) {
    super();
    this.container = options.container;
    this.logger = options.logger;
  }

  /**
   * Helper method to invoke the function exported by `config/routes.js` in the context of the
   * current router instance.
   */
  public map(fn: (router: Router) => void): void {
    debug('mapping routes');
    fn(this);
  }

  /**
   * Takes an incoming request and it's response from an HTTP server, prepares them, runs the
   * generic middleware first, hands them off to the appropriate action given the incoming URL, and
   * finally renders the response.
   */
  public async handle(req: IncomingMessage, res: ServerResponse): Promise<void> {
    let request = new Request(req);
    try {

      debug(`[${ request.id }]: ${ request.method.toUpperCase() } ${ request.path }`);
      await fromNode((cb) => this.middleware.run(req, res, cb));

      debug(`[${ request.id }]: routing request`);
      let routes = this.routes[request.method];
      for (let i = 0; i < routes.length; i += 1) {
        request.params = routes[i].match(request.path);
        if (request.params) {
          request.route = routes[i];
          break;
        }
      }
      if (!request.route) {
        debug(`[${ request.id }]: ${ request.method } ${ request.path } did match any route. Available ${ request.method } routes:\n${ routes.map((r) => r.spec).join(',\n') }`);
        throw new Errors.NotFound('Route not recognized');
      }

      // eslint-disable-next-line new-cap
      let action: Action = new (<any>request.route.action)({
        request,
        logger: this.logger,
        container: this.container
      });

      let serializer: Serializer | false;
      if (action.serializer != null) {
        if (typeof action.serializer === 'string') {
          serializer = this.container.lookup(`serializer:${ action.serializer }`);
        } else if (action.serializer === false) {
          serializer = null;
        } else {
          serializer = <Serializer | false>action.serializer;
        }
      } else {
        serializer = this.container.lookup('serializer:application');
      }

      if (typeis.hasBody(request) && serializer) {
        debug(`[${ request.id }]: parsing request body`);
        request.body = serializer.parse(request.body);
      }

      debug(`[${ request.id }]: running action`);
      let response = await action.run();

      debug(`[${ request.id }]: setting response status code to ${ response.status }`);
      res.statusCode = response.status;
      res.setHeader('X-Request-Id', request.id);

      if (response.body) {
        debug(`[${ request.id }]: writing response body`);
        res.setHeader('Content-type', response.contentType);
        if (this.container.config.environment !== 'production') {
          res.write(JSON.stringify(response.body, null, 2));
        } else {
          res.write(JSON.stringify(response.body));
        }
      }

      res.end();
      debug(`[${ request.id }]: response finished`);

    } catch (error) {
      return this.handleError(request, res, error);
    }
  }

  /**
   * Takes a request, response, and an error and hands off to the generic application level error
{'fraction_non_alphanumeric': 0.14272689598010774, 'fraction_numerical': 0.0004973062577704103, 'mean_word_length': 3.0341023069207624, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 19, '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': '6775760', 'n_tokens_mistral': 3469, 'n_tokens_neox': 3430, 'n_words': 1068}