UNPKG

@sauce-api/core

Version:

Sauce API core functionality

241 lines (240 loc) 8.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RouteValidator = void 0; const utility_1 = require("../utility"); const Policies_1 = require("../routes/Policies"); class RouteValidator { /** * @param {Sauce<custom>} Sauce instance of Sauce class. * @return {void} */ constructor(Sauce) { this.Sauce = Sauce; this.req = Sauce.req; this.currentRoute = Sauce.currentRoute; this.res = Sauce.res; this.next = Sauce.next; this.route = Sauce.currentRoute; this.config = Sauce.config; } /** * Checks the policies for the current route. * @param {policyList<custom>} policyList policies defined for the route. * @return {Promise<void>} */ async checkPolicies(policyList) { const policies = new Policies_1.Policies(policyList); if (this.route.policies) { await (0, utility_1.asyncForEach)(this.route.policies, async (policyName) => { await policies.runPolicy(policyName, this.Sauce); }); } } /** * Runs validations for path, query and request body params. * @return {Promise<void>} */ async runValidations() { try { await Promise.all([ this.validatePathParams(), this.validateQueryParams(), this.validateReqBody() ]); } catch (e) { throw e; } } /** * Validates the path parameters. * @return {Promise<RouteParam[]>} */ async validatePathParams() { try { if (this.route.pathParams) { return (0, utility_1.asyncForEach)(this.route.pathParams, param => { const pathParam = this.req.params[param.name]; this.validatePathParamType(param, pathParam); this.req.params[param.name] = this.convertType(param.type, pathParam); this.runCustomParamValidator(param, pathParam); }); } } catch (e) { throw e; } } /** * Validates the query parameters. * @return {Promise<RouteParam[]>} */ async validateQueryParams() { try { if (this.route.queryParams) { return (0, utility_1.asyncForEach)(this.route.queryParams, param => { const queryParam = this.req.query[param.name]; this.validateRequiredParam(param, queryParam); if (typeof queryParam != "undefined") { this.validateParamType(param, queryParam); this.req.query[param.name] = this.convertType(param.type, queryParam); this.validateEnumParam(param, queryParam); } this.runCustomParamValidator(param, queryParam); }); } } catch (e) { throw e; } } /** * Validates the request body parameters. * @return {Promise<RouteParam[]>} */ async validateReqBody() { try { if (this.route.bodySchema) { if (!this.req.body) { throw (0, utility_1.formatError)(400, "Payload is expected"); } return this.validateReqBodyParams(this.route.bodySchema, null, this.req.body); } } catch (e) { throw e; } } async validateReqBodyParams(schema, ancestor, obj) { return (0, utility_1.asyncForEach)(schema, param => this.validateReqBodyParam(param, ancestor, obj)); } async validateReqBodyParam(schemaLevel, ancestor, obj) { if (ancestor) { if (Array.isArray(obj)) { await (0, utility_1.asyncForEach)(obj, row => this.processBodyReqRow(schemaLevel, row)); } else { this.processBodyReqRow(schemaLevel, obj); } } else { this.processBodyReqRow(schemaLevel, this.req.body); } if (schemaLevel.children) { if (Array.isArray(obj)) { await (0, utility_1.asyncForEach)(obj, row => this.validateReqBodyParams(schemaLevel.children, schemaLevel, row[schemaLevel.name])); } else { await this.validateReqBodyParams(schemaLevel.children, schemaLevel, obj[schemaLevel.name]); } } } processBodyReqRow(schemaLevel, row) { this.validateRequiredParam(schemaLevel, row[schemaLevel.name]); if (typeof row[schemaLevel.name] != "undefined") { this.validateParamType(schemaLevel, row[schemaLevel.name]); row[schemaLevel.name] = this.convertType(schemaLevel.type, row[schemaLevel.name]); } this.runCustomParamValidator(schemaLevel, row[schemaLevel.name]); } runCustomParamValidator(param, requestParamValue) { if (param.customValidator) { param.customValidator(requestParamValue, this.req); } } validateRequiredParam(param, requestParam) { if (param.required && !requestParam) { const err = `${param.name} was not sent and is required`; throw (0, utility_1.formatError)(400, err); } } validateEnumParam(param, requestParam) { if (requestParam && param.type === "enum") { const filtered = param.enumValues.filter((val) => val === requestParam); if (filtered.length === 0) { const err = `Invalid enum value for ${param.name}: ${requestParam}`; throw (0, utility_1.formatError)(400, err); } } } validateParamType(param, requestParam) { if (requestParam) { if (!this.isValidTypes(param.type, requestParam)) { const err = `Invalid param type for ${param.name}: Expected ${param.type} but got ${typeof requestParam}`; throw (0, utility_1.formatError)(400, err); } } } validatePathParamType(param, requestParam) { if (requestParam) { if (!this.isValidTypes(param.type, requestParam)) { const err = `${this.route.method.toString().toUpperCase()} ${this.req.path} is not a valid request path`; throw (0, utility_1.formatError)(400, err); } } } isValidTypes(type, paramValue) { let isValid = true; switch (type) { case "boolean": try { const value = typeof paramValue == "string" ? this.parseBool(paramValue) : paramValue; if (typeof value !== "boolean") { isValid = false; } } catch (e) { isValid = false; } break; case "number": if (isNaN(parseInt(paramValue))) { isValid = false; } break; case "object": if (!(typeof paramValue === "object")) { isValid = false; } break; case "string": if (typeof paramValue != "string") { isValid = false; } break; case "array": if (!Array.isArray(paramValue)) { isValid = false; } break; default: isValid = true; } return isValid; } convertType(type, paramValue) { switch (type) { case "boolean": return this.parseBool(paramValue); case "number": return (+paramValue); default: return paramValue; } } parseBool(stringIn) { try { if (stringIn === "false") { return false; } if (stringIn === "true") { return true; } throw Error("Invalid value supplied"); } catch (e) { throw e; } } } exports.RouteValidator = RouteValidator;