UNPKG

nope-js-browser

Version:

NoPE Runtime for the Browser. For nodejs please use nope-js-node

222 lines (221 loc) 9.93 kB
/** * @author Martin Karkowski * @email m.karkowski@zema.de * @create date 2021-11-11 11:36:37 * @modify date 2021-11-11 11:36:37 * @desc [description] */ import { readFile } from "fs/promises"; import * as handlebars from "handlebars"; import { join, relative } from "path"; import { convertPathToOsPath, createFile, createPath, } from "../../helpers/fileMethods"; import { deepClone } from "../../helpers/objectMethods"; import { replaceAll } from "../../helpers/stringMethods"; /** * Helper function to merge the Parameters into 1 description. * @param elements */ function _unifySchema(elements, options) { const ret = { type: "object", properties: {}, required: [], }; for (const item of elements) { if (options.sharedDefinitions) { item.schema.definitions = undefined; item.schema.$schema = undefined; } // Assign the Schema ret.properties[item.name] = item.schema; // If the Element is Optional, if (!item.optional) { ret.required.push(item.name); } } return ret; } /** * Function, to parse a description to an Open-API Element. * @param description * @param options */ export async function parseModuleToOpenAPI(description, options) { const _description = deepClone(description); // load the Template. const template = await readFile(join(process.cwd(), "lib", "parsers", "open-api", "templates", "method." + options.mode + ".handlebars"), { encoding: "utf-8", }); // Renderfuncting const renderAPI = handlebars.compile(template); await createPath(join(options.outputDir)); // Now iterate over the Methods of the Module and find parsable Methods. for (const [idx, method] of _description.methods.entries()) { // Test if the Method contains some functions in the input / Output: const parsedInputs = JSON.stringify(method.schema.inputs); const parsedOutput = JSON.stringify(method.schema.outputs); if (!parsedInputs.includes('"function"') && !parsedOutput.includes('"function"')) { // The Method should be parseable. // 1. Specify the Mode (No Params = GET, else POST) method.mode = method.schema.inputs.length > 0 ? "POST" : "GET"; method.required = method.schema.inputs .filter((param) => { return !param.optional; }) .map((param) => param.name); method.hasInput = method.schema.inputs.length > 0; // Now adapt the Schema of the Method. // Iterate over the Inputs, add a Description if not provided // And determine whehter the Parameters are required or not. method.schema.inputs = method.schema.inputs.map((param) => { // Provide a Description. (If not provided) param.description = param.description || "Not provided"; if (options.sharedDefinitions) { param.schema.definitions = undefined; param.schema.$schema = undefined; } // Parse the Schema in here. param.parsedSchema = JSON.stringify(param.schema); return param; }); // Make shure the Return type isnt an array method.schema.outputs = Array.isArray(method.schema.outputs) ? _unifySchema(method.schema.outputs, options) : method.schema.outputs; if (options.sharedDefinitions) { method.schema.outputs.definitions = undefined; method.schema.outputs.$schema = undefined; } // Add an Description to the result. method.resultDescription = method.schema.outputs.description || "Not Provided"; // And add a Schema for the Return type. method.parsedOutput = JSON.stringify(method.schema.outputs); method.parsedInput = JSON.stringify(_unifySchema(method.schema.inputs, options)); method.hasInput = method.schema.inputs.length > 0; method.tag = description.name; // Determine the Filename. const fileDir = join(options.outputDir, _description.name, "{instance}", "methods"); const fileName = join(fileDir, method.id + "." + options.mode); // Determine the Import Pathes. const imports = [ { dir: join(process.cwd(), "lib", "types", "nope"), fileName: "nopeDispatcher.interface", name: "pathOfDispatcher", }, { dir: join(process.cwd(), "lib", "helpers"), fileName: "dispatcherPathes", name: "pathOfHelper", }, ]; for (const imp of imports) { const relativDir = relative(fileDir, imp.dir); method[imp.name] = join(convertPathToOsPath(relativDir), convertPathToOsPath(imp.fileName)); } // Write down the Schema: await createFile( // Generate the Path. fileName, renderAPI(method)); if (options.logger) { options.logger.info("Generated -> " + fileName); } } else if (options.logger) { options.logger.warn('parser can not convert: "' + description.name + "." + method.id + '". The Function contains functions as parameters'); } } } /** * Function, to parse a description to an Open-API Element. * @param description * @param options */ export async function parseFunctionToOpenAPI(description, options) { const _description = deepClone(description); // load the Template. const template = await readFile(join(process.cwd(), "lib", "parsers", "open-api", "templates", "function." + options.mode + ".handlebars"), { encoding: "utf-8", }); // Renderfuncting const renderAPI = handlebars.compile(template); await createPath(join(options.outputDir)); // Now iterate over the Methods of the Module and find parsable Methods. // Test if the Method contains some functions in the input / Output: const parsedInputs = JSON.stringify(_description.schema.inputs); const parsedOutput = JSON.stringify(_description.schema.outputs); if (!parsedInputs.includes('"function"') && !parsedOutput.includes('"function"')) { // The Method should be parseable. // 1. Specify the Mode (No Params = GET, else POST) _description.mode = _description.schema.inputs.length > 0 ? "POST" : "GET"; // Now adapt the Schema of the Method. // Iterate over the Inputs, add a Description if not provided // And determine whehter the Parameters are required or not. _description.schema.inputs = _description.schema.inputs.map((param) => { // Provide a Description. (If not provided) param.description = param.description || "Not provided"; if (options.sharedDefinitions) { param.schema.definitions = undefined; param.schema.$schema = undefined; } // Parse the Schema in here. param.parsedSchema = JSON.stringify(param.schema); return param; }); // Make shure the Return type isnt an array _description.schema.outputs = Array.isArray(_description.schema.outputs) ? _unifySchema(_description.schema.outputs, options) : _description.schema.outputs; if (options.sharedDefinitions) { _description.schema.outputs.definitions = undefined; _description.schema.outputs.$schema = undefined; } // Add an Description to the result. _description.resultDescription = _description.schema.outputs.description || "Not Provided"; // And add a Schema for the Return type. _description.parsedOutput = JSON.stringify(_description.schema.outputs); _description.tag = "generic-services"; _description.parsedInput = JSON.stringify(_unifySchema(_description.schema.inputs, options)); _description.hasInput = _description.schema.inputs.length > 0; _description.required = _description.schema.inputs .filter((param) => !param.optional) .map((param) => param.name); _description.name = _description.id; // Determine the Filename. const fileDir = join(options.outputDir, "generic-services"); const fileName = join(fileDir, _description.id + "." + options.mode); // Determine the Import Pathes. const imports = [ { dir: join(process.cwd(), "lib", "types", "nope"), fileName: "nopeDispatcher.interface", name: "pathOfDispatcher", }, ]; for (const imp of imports) { const relativDir = relative(fileDir, imp.dir); _description[imp.name] = replaceAll(join(relativDir, imp.fileName), "\\", "/"); } // Write down the Schema: await createFile( // Generate the Path. fileName, renderAPI(_description)); if (options.logger) { options.logger.info("Generated -> " + fileName); } } else if (options.logger) { options.logger.warn('parser can not convert: "' + _description.id + '". The Function contains functions as parameters'); } }