lib_Endpoint.js

import debug from 'debug';
import ParameterValidator from './ParameterValidator.js';
import SecurityValidator from './SecurityValidator.js';
import ResponseValidator from './ResponseValidator.js';

/**
 * Endpoint instance (combines ParameterValidator + SecurityValidator)
 * @module Endpoint
 */
export default class Endpoint {
  /**
   * Endpoint constructor
   * @param {Object[]} securitySchemes                 openapi top-level security definition
   * @param {Map<string, function>} securityCallbacks  matching security definition validators
   * @param {object} definition                        openapi endpoint definition
   * @param {Map<string, function>} controllerFunc     matching endpoint controller function
   * @param {boolean} enforceResponseValidation        flag for failing invalid responses (that don't match the endpoint's responseScheme)
   * @see {@link https://swagger.io/docs/specification/authentication/}
   * @see {@link https://swagger.io/docs/specification/paths-and-operations/}
   */
  constructor(securitySchemes, securityCallbacks, definition, controllerFunc, enforceResponseValidation) {
    this.securitySchemes = securitySchemes;
    this.securityCallbacks = securityCallbacks;
    this.endpointDefinition = definition;
    this.controllerFunc = controllerFunc;
    this.enforceResponseValidation = enforceResponseValidation;

    this.debug = debug('openapi:endpoint');

    this.parseDefinition();
  }

  /**
   * Attach the relevant validators to this endpoint
   * @private
   */
  parseDefinition() {
    const requestBodyMap = this.endpointDefinition.requestBody ? Object.keys(this.endpointDefinition.requestBody.content).reduce((all, contentType) => ({
      ...all,
      [contentType]: this.endpointDefinition.requestBody.content[contentType].schema,
    }), {}) : null;
    this.paramValidator = new ParameterValidator(this.endpointDefinition.parameters, requestBodyMap);
    if (this.endpointDefinition.responses) {
      this.responseValidator = new ResponseValidator(this.endpointDefinition.responses, this.enforceResponseValidation);
    }
    if (this.endpointDefinition.security) {
      this.securityValidator = new SecurityValidator(this.securitySchemes, this.endpointDefinition.security, this.securityCallbacks);
    }
  }

  /**
   * Test request against the endpoint input validators
   * @param {Map<string, any>} path           path parameters object
   * @param {Map<string, any>} headers        header parameters object
   * @param {Map<string, any>} queryParams    query string parameters object
   * @param {string} contentType              content type of the input
   * @param {Map<string, any>} bodyParams     body
   * @throws {module:ParameterError|module:SecurityError}
   */
  async testIncoming(path, headers, queryParams, contentType, bodyParams) {
    this.paramValidator.test(path, queryParams, { [contentType]: bodyParams });
    if (this.securityValidator) {
      await this.securityValidator.test(headers, queryParams);
    }
  }

  /**
   * Test response against the endpoint output validators
   * @param {string} statusCode       response status code
   * @param {string} contentType      response content type
   * @param {object} responseBody     response body
   * @return {Promise<ResponseError|boolean>}
   */
  async testOutgoing(statusCode, contentType, responseBody) {
    if (this.responseValidator) {
      return this.responseValidator.test(statusCode, contentType, responseBody);
    }

    return true;
  }
}