import { copyFile, readFile } from "fs/promises"; import * as handlebars from 'handlebars'; import { join } from 'path'; import { Project } from "ts-morph"; import { Logger } from 'winston'; import { IJsonSchema } from "../../types/IJSONSchema"; import { IExportMethodToOpenAPIParameters } from "../openapi/nopeOpenAPIDecorators"; import { DecoratorInformation, MethodInformation } from "./analyzeTypescriptFiles"; import { createFile, createPath } from "./fileHelpers"; import { generateSchemas } from "./generateSchemas"; import { schemaGetDefinition } from "./jsonSchemaMethods"; function generateSchema(methodName: string, schema: IJsonSchema) { return JSON.stringify(schemaGetDefinition(schema, "#/definitions/" + methodName), undefined, 4); } /** * Generate the Client Templates. * @param options */ export async function generateOpenAPI(options: { pathToSchemaTemplate: string, pathToApiTemplate: string, tempDir: string, outputDir: string, inputDir: string, tsConfigFilePath: string, logger?: Logger, }) { // Create the Output dir (if it doenst exists) const { schemaMapping, analysis } = await generateSchemas(options) // load the File. const typescriptTemplate = await readFile(options.pathToApiTemplate, { encoding: 'utf-8' }) // Renderfuncting const renderAPI = handlebars.compile(typescriptTemplate); const methods: (IExportMethodToOpenAPIParameters & MethodInformation & DecoratorInformation & { className: string, baseUri: string, methodUri: string, useCustomParameters: boolean, useDefaultParameters: boolean, customParameters: string, inputSchema: string, outputSchema: string })[] = [] await createPath(join(options.outputDir)); // Firstly copy the nopeDispatcher await copyFile( join(__dirname, '..', '..', '..', 'lib', 'dispatcher', 'nopeDispatcher.ts'), join(options.outputDir, 'nopeDispatcher.ts') ); for (const relevantClass of analysis) { for (const method of relevantClass.methods) { const settings: { className: string, baseUri: string, methodUri: string, useCustomParameters: boolean, useDefaultParameters: boolean, customParameters: string, inputSchema: string, outputSchema: string } = { className: relevantClass.className, baseUri: relevantClass.classDecorator.decoratorSettings.exportsElementsToOpenAPI.uri || relevantClass.className, methodUri: method.decoratorSettings.exportMethodToOpenAPI.uri || method.name, customParameters: JSON.stringify(method.decoratorSettings.exportMethodToOpenAPI.parameters || [], undefined, 4), useCustomParameters: Array.isArray(method.decoratorSettings.exportMethodToOpenAPI.parameters), useDefaultParameters: method.params.length > 0, inputSchema: generateSchema(method.name + 'Input', schemaMapping[relevantClass.className]), outputSchema: generateSchema(method.name + 'Output', schemaMapping[relevantClass.className]), } const item = Object.assign( method, { method: method.params.length > 0 ? 'POST' : 'GET', operationId: settings.className + method.name, }, settings, method.decoratorSettings.exportMethodToOpenAPI as IExportMethodToOpenAPIParameters ); methods.push(item); const fileName = join(options.outputDir, item.baseUri, item.methodUri + '.ts'); // Write down the Schema: await createFile( // Generate the Path. fileName, renderAPI(item) ); if (options.logger) { options.logger.info('Generated -> ' + fileName); } // Function to Determine new project files. const project = new Project({ tsConfigFilePath: options.tsConfigFilePath, addFilesFromTsConfig: false, }); project.addSourceFileAtPath(fileName); // Readin the Source-Files. const sourceFiles = project.getSourceFiles(); for (const file of sourceFiles) { file.formatText(); await file.save(); } } } return schemaMapping; }