"use strict"; module.exports = validate; const { RequestError } = require("@octokit/request-error"); const get = require("lodash.get"); const set = require("lodash.set"); function validate(octokit, options) { if (!options.request.validate) { return; } const { validate: params } = options.request; Object.keys(params).forEach(parameterName => { const parameter = get(params, parameterName); const expectedType = parameter.type; let parentParameterName; let parentValue; let parentParamIsPresent = true; let parentParameterIsArray = false; if (/\./.test(parameterName)) { parentParameterName = parameterName.replace(/\.[^.]+$/, ""); parentParameterIsArray = parentParameterName.slice(-2) === "[]"; if (parentParameterIsArray) { parentParameterName = parentParameterName.slice(0, -2); } parentValue = get(options, parentParameterName); parentParamIsPresent = parentParameterName === "headers" || (typeof parentValue === "object" && parentValue !== null); } const values = parentParameterIsArray ? (get(options, parentParameterName) || []).map( value => value[parameterName.split(/\./).pop()] ) : [get(options, parameterName)]; values.forEach((value, i) => { const valueIsPresent = typeof value !== "undefined"; const valueIsNull = value === null; const currentParameterName = parentParameterIsArray ? parameterName.replace(/\[\]/, `[${i}]`) : parameterName; if (!parameter.required && !valueIsPresent) { return; } // if the parent parameter is of type object but allows null // then the child parameters can be ignored if (!parentParamIsPresent) { return; } if (parameter.allowNull && valueIsNull) { return; } if (!parameter.allowNull && valueIsNull) { throw new RequestError( `'${currentParameterName}' cannot be null`, 400, { request: options } ); } if (parameter.required && !valueIsPresent) { throw new RequestError( `Empty value for parameter '${currentParameterName}': ${JSON.stringify( value )}`, 400, { request: options } ); } // parse to integer before checking for enum // so that string "1" will match enum with number 1 if (expectedType === "integer") { const unparsedValue = value; value = parseInt(value, 10); if (isNaN(value)) { throw new RequestError( `Invalid value for parameter '${currentParameterName}': ${JSON.stringify( unparsedValue )} is NaN`, 400, { request: options } ); } } if (parameter.enum && parameter.enum.indexOf(String(value)) === -1) { throw new RequestError( `Invalid value for parameter '${currentParameterName}': ${JSON.stringify( value )}`, 400, { request: options } ); } if (parameter.validation) { const regex = new RegExp(parameter.validation); if (!regex.test(value)) { throw new RequestError( `Invalid value for parameter '${currentParameterName}': ${JSON.stringify( value )}`, 400, { request: options } ); } } if (expectedType === "object" && typeof value === "string") { try { value = JSON.parse(value); } catch (exception) { throw new RequestError( `JSON parse error of value for parameter '${currentParameterName}': ${JSON.stringify( value )}`, 400, { request: options } ); } } set(options, parameter.mapTo || currentParameterName, value); }); }); return options; }