@sauce-api/core
Version:
Sauce API core functionality
241 lines (240 loc) • 8.33 kB
JavaScript
"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;