UNPKG

somod-http-extension

Version:

SOMOD middleware to intercept and validate Lambda event for AWS APIGateway

83 lines (82 loc) 4.42 kB
import { readYamlFileStore, readJsonFileStore } from "nodejs-file-utils"; import { join } from "path"; import { FILE_ROUTES_HTTP_JSON, FILE_TEMPLATE_YAML, PATH_BUILD, PATH_FUNCTIONS, PATH_HTTP_SCHEMAS, PATH_NODE_MODULES, PATH_SERVERLESS, PATH_SOMOD_WORKING_DIR, RESOURCE_TYPE_FUNCTION } from "./constants"; import { existsSync } from "fs"; import { copyFile, mkdir, writeFile } from "fs/promises"; import { getHttpSchemaPath } from "./utils"; import { getCompiledValidator } from "decorated-ajv"; const getFunctionMeta = function (CodeUri) { // Reffering to this code to find the module and function name // https://github.com/somod-dev/somod/blob/main/packages/lib/src/utils/serverless/keywords/function.ts#L278-L279 const codeUriParts = CodeUri.split("/"); const functionName = codeUriParts.pop(); let moduleName = codeUriParts.pop(); if (codeUriParts[codeUriParts.length - 1] != PATH_FUNCTIONS) { moduleName = codeUriParts.pop() + "/" + moduleName; } return { name: functionName, module: moduleName }; }; const getRoutesHttpJsonPath = (dir, rootModuleName, name, module) => { const routesJsonPath = [ PATH_BUILD, PATH_SERVERLESS, PATH_FUNCTIONS, name + ".http.json" ]; if (module != rootModuleName) { routesJsonPath.unshift(PATH_NODE_MODULES, module); } routesJsonPath.unshift(dir); return join(...routesJsonPath); }; const getFunctionsWithRoutes = async (context) => { const templateYaml = (await readYamlFileStore(join(context.dir, FILE_TEMPLATE_YAML))); const functionResources = Object.values(templateYaml.Resources).filter(resource => resource.Type == RESOURCE_TYPE_FUNCTION && !!resource.Properties.CodeUri // Does not work with InlineCode in the Template. Function must have CodeUri instead ); const functionsMeta = functionResources.map(resource => getFunctionMeta(resource.Properties.CodeUri)); return functionsMeta.filter(({ name, module }) => existsSync(getRoutesHttpJsonPath(context.dir, context.moduleHandler.rootModuleName, name, module))); }; const insertCompiledHttpSchemaIntoFunction = async (context, name, module) => { const routesJsonPath = getRoutesHttpJsonPath(context.dir, context.moduleHandler.rootModuleName, name, module); const routes = (await readJsonFileStore(routesJsonPath)); const httpSchemasPath = join(context.dir, PATH_SOMOD_WORKING_DIR, PATH_SERVERLESS, PATH_FUNCTIONS, module, name, PATH_HTTP_SCHEMAS); await mkdir(httpSchemasPath, { recursive: true }); await copyFile(routesJsonPath, join(httpSchemasPath, FILE_ROUTES_HTTP_JSON)); await Promise.all(Object.keys(routes).map(async (path) => { await Promise.all(Object.keys(routes[path]).map(async (method) => { const parameters = routes[path][method].parameters; if (parameters !== undefined) { await Promise.all(parameters.map(async (parameter) => { const schemaFileName = getHttpSchemaPath(path, method, { in: parameter.in, name: parameter.name }); const compiledSchema = await getCompiledValidator(parameter.schema); await writeFile(join(httpSchemasPath, schemaFileName), compiledSchema); })); } const body = routes[path][method].body; if (body) { const schemaFileName = getHttpSchemaPath(path, method, "body"); const compiledSchema = await getCompiledValidator(body.schema); await writeFile(join(httpSchemasPath, schemaFileName), compiledSchema); } })); })); }; /** * parse template.yaml and find AWS::Serverless::Function * get all module names and for each module get routes configuration * generate schema compiled files and save inside .somod/serverless/functions/<module>/<function>/http-schemas/ */ export const prepare = async (context) => { if (!existsSync(join(context.dir, FILE_TEMPLATE_YAML))) { // eslint-disable-next-line no-console console.log("Skipping prepare. No template.yaml found"); return; } const functionsWithRoutes = await getFunctionsWithRoutes(context); await Promise.all(functionsWithRoutes.map(async ({ name, module }) => { await insertCompiledHttpSchemaIntoFunction(context, name, module); })); };