UNPKG

somod-http-extension

Version:

SOMOD middleware to intercept and validate Lambda event for AWS APIGateway

90 lines (89 loc) 4.3 kB
import { join } from "path"; import { KEYWORD_SOMOD_FUNCTION, PATH_FUNCTIONS, PATH_SERVERLESS, RESOURCE_TYPE_FUNCTION, SOMOD_HTTP_EXTENSION } from "./constants"; import { readdir } from "fs/promises"; import { existsSync } from "fs"; import { getValidator, validate } from "decorated-ajv"; import { schema as httpYamlSchema } from "./routes-schema"; import { readYamlFileStore } from "nodejs-file-utils"; import chalk from "chalk"; /** * Validates the schema of all `serverless/functions/<function_name>.http.yaml` * */ export const validateHttpYamlFilesSchema = async (context) => { const functionsDir = join(context.dir, PATH_SERVERLESS, PATH_FUNCTIONS); if (!existsSync(functionsDir)) { // don't do anything if functionsDir does not exist return; } const httpYamlFiles = (await readdir(functionsDir)).filter(f => f.endsWith(".http.yaml")); const httpYamlFileViolations = {}; const schemaValidator = await getValidator(httpYamlSchema); for (const httpYamlFile of httpYamlFiles) { const yamlContent = await readYamlFileStore(join(functionsDir, httpYamlFile)); const violations = await validate({}, yamlContent, schemaValidator); if (violations.length > 0) { httpYamlFileViolations[httpYamlFile] = violations; } } if (Object.keys(httpYamlFileViolations).length > 0) { throw new Error(`Error validating the .http.yaml files : (from ${SOMOD_HTTP_EXTENSION}): \n${Object.keys(httpYamlFileViolations).map(httpYamlFile => { return ` ${httpYamlFile} has following errors\n${httpYamlFileViolations[httpYamlFile].map(violation => { return ` ${violation.message} at ${violation.path}`; })}`; })}`); } }; /** * Checks if the `.http.yaml` exists for functions that have somod-http-extension middleware * * | YAML | Middleware | Result | * | -----| -----------| ---- | * | No | No | Valid | * | No | Yes | Error | * | Yes | No | Warning | * | Yes | Yes | Valid | * */ export const validateHttpYamlFilesForMiddlewares = async (context) => { const template = context.serverlessTemplateHandler.getTemplate(context.moduleHandler.rootModuleName); if (template == null) { return; } const functionsDir = join(context.dir, PATH_SERVERLESS, PATH_FUNCTIONS); const functionsWithHttpYamlFiles = existsSync(functionsDir) ? (await readdir(functionsDir)) .filter(f => f.endsWith(".http.yaml")) .map(f => f.substring(0, f.length - ".http.yaml".length)) : []; const functionsWithHttpYamlFilesMap = Object.fromEntries(functionsWithHttpYamlFiles.map(f => [f, true])); const functionsWithHttpMiddleware = Object.values(template.template.Resources) .filter(r => r.Type == RESOURCE_TYPE_FUNCTION) .map(r => ({ name: r.Properties["CodeUri"]?.[KEYWORD_SOMOD_FUNCTION]?.name || "", middlewares: (r.Properties["CodeUri"]?.[KEYWORD_SOMOD_FUNCTION]?.["middlewares"] || []) })) .filter(f => { return !f.middlewares.every(m => !(m.module == SOMOD_HTTP_EXTENSION && m.resource == "SomodHttpMiddleware")); }) .map(f => f.name); const functionsWithHttpMiddlewareMap = Object.fromEntries(functionsWithHttpMiddleware.map(f => [f, true])); const functionsWithNoFile = functionsWithHttpMiddleware.filter(f => !functionsWithHttpYamlFilesMap[f]); if (functionsWithNoFile.length > 0) { throw new Error(`Error: Following functions does not have .http.yaml files (from ${SOMOD_HTTP_EXTENSION})\n${functionsWithNoFile .map(f => ` ${f}`) .join("\n")}`); } const functionsWithNoMiddleware = functionsWithHttpYamlFiles.filter(f => !functionsWithHttpMiddlewareMap[f]); if (functionsWithNoMiddleware.length > 0) { // eslint-disable-next-line no-console console.warn(chalk.yellow(`Warning: Following functions does not have SomodHttpMiddleware (from ${SOMOD_HTTP_EXTENSION})\n${functionsWithNoMiddleware .map(f => ` ${f}`) .join("\n")}`)); } }; export const prebuild = async (context) => { await validateHttpYamlFilesForMiddlewares(context); await validateHttpYamlFilesSchema(context); };