Fixing Open-Api Creation.
Provinding Swagger UI.
This commit is contained in:
parent
8787a7cc75
commit
d325c07c6c
2
api/.gitignore
vendored
Normal file
2
api/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Logs
|
||||
backend
|
30
lib/cli/generateFiles.ts
Normal file
30
lib/cli/generateFiles.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { readFile } from 'fs/promises';
|
||||
import { generateOpenAPI } from "../helpers/generateOpenAPI";
|
||||
import { generateClientTemplate } from "../helpers/generateTemplate";
|
||||
import { getLogger } from '../logger/getLogger';
|
||||
|
||||
const main = async function () {
|
||||
|
||||
// Read the Config in.
|
||||
const config = JSON.parse(
|
||||
await readFile(
|
||||
'./nopeconfig.json',
|
||||
{
|
||||
encoding: 'utf8'
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
// Define a Logger
|
||||
const logger = getLogger('info');
|
||||
|
||||
logger.info('Creating Dispatched API');
|
||||
|
||||
await generateClientTemplate(Object.assign(config.dispatcher, { logger }));
|
||||
|
||||
logger.info('Creating Open-API');
|
||||
|
||||
await generateOpenAPI(Object.assign(config.openapi, { logger }))
|
||||
}
|
||||
|
||||
main().catch(e => console.error(e));
|
@ -233,7 +233,10 @@ function _getType(node: Node, inputType: Type, text: string) {
|
||||
};
|
||||
}));
|
||||
|
||||
} else if (Node.isFunctionDeclaration(node) || Node.isFunctionTypeNode(node) || Node.isFunctionLikeDeclaration(node)) {
|
||||
}
|
||||
|
||||
// The Element could be a Function.
|
||||
if (Node.isFunctionDeclaration(node) || Node.isFunctionTypeNode(node) || Node.isFunctionLikeDeclaration(node)) {
|
||||
baseType = "function";
|
||||
|
||||
// Iterate over the parameter
|
||||
@ -389,6 +392,11 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla
|
||||
|
||||
// Extract the Parameters of the Function
|
||||
const params: ParameterInformation[] = declaration.getParameters().map((parameter, index) => {
|
||||
|
||||
// Based on the Fact, whether the Parameter is a Function or not
|
||||
// The inputs of the the _getType Function has to be adapted.
|
||||
const isFunc = Node.isMethodDeclaration(parameter)
|
||||
|
||||
return Object.assign(
|
||||
{
|
||||
// Name of the parameter
|
||||
@ -405,7 +413,7 @@ export function getDescription(declaration: PropertyDeclaration | InterfaceDecla
|
||||
_getType(
|
||||
parameter.getTypeNode(),
|
||||
parameter.getType(),
|
||||
parameter.getText()
|
||||
isFunc ? parameter.getText() : parameter.getType().getText()
|
||||
)
|
||||
)
|
||||
});
|
||||
|
@ -40,49 +40,52 @@ export async function createFile(fileName: string, content: string, options?: (B
|
||||
* @param options The options to write the file. See original docu: https://nodejs.org/dist/latest-v8.x/docs/api/fs.html#fs_fs_writefile_file_data_options_callback
|
||||
*/
|
||||
export async function createPath(path: string) {
|
||||
// Based on the OS select the Path Element.
|
||||
const SPLITCHAR = type() === 'Linux' ? '/' : '\\';
|
||||
// Adapt the File Pathes
|
||||
path = type() === 'Linux' ? path.replace(/\\\\/g, '/') : path.replace(/\//g, '\\');
|
||||
|
||||
// Split the Path into different segements.
|
||||
const pathParts = path.split('/');
|
||||
return await mkdir(path, { recursive: true })
|
||||
|
||||
if (pathParts.length > 0) {
|
||||
/** Check if the given Path Ends with a File Name */
|
||||
if (pathParts[pathParts.length - 1].indexOf('.') !== -1) {
|
||||
pathParts.pop();
|
||||
}
|
||||
// // Based on the OS select the Path Element.
|
||||
// const SPLITCHAR = type() === 'Linux' ? '/' : '\\';
|
||||
// // Adapt the File Pathes
|
||||
// path = type() === 'Linux' ? path.replace(/\\\\/g, '/') : path.replace(/\//g, '\\');
|
||||
|
||||
// Reassemble the Segments like that:
|
||||
//
|
||||
// C:\\Test\\SubFolder\\AnotherFolder
|
||||
// Split up:
|
||||
// => fileParts = [C: , Test , SubFolder, AnotherFolder]
|
||||
//
|
||||
// Reassemble:
|
||||
// 1) C:
|
||||
// 2) C:\\Test
|
||||
// 3) C:\\Test\\SubFolder
|
||||
// 4) ...
|
||||
//
|
||||
// Everytime after an assembly Check whether the assembled
|
||||
// path exists, otherwise create that folder and go on.
|
||||
// // Split the Path into different segements.
|
||||
// const pathParts = path.split('/');
|
||||
|
||||
for (const [idx, folder] of pathParts.entries()) {
|
||||
// Assemble the Path.
|
||||
const currentPath = pathParts.slice(0, idx + 1).join(SPLITCHAR);
|
||||
// Test and create the Folder
|
||||
const urlExists = await _exists(currentPath);
|
||||
// Test if the provided Path exists or not.
|
||||
if (currentPath && !urlExists) {
|
||||
await mkdir(currentPath);
|
||||
} else if (currentPath && urlExists && (await _lstat(currentPath)).isFile()) {
|
||||
// if (pathParts.length > 0) {
|
||||
// /** Check if the given Path Ends with a File Name */
|
||||
// if (pathParts[pathParts.length - 1].indexOf('.') !== -1) {
|
||||
// pathParts.pop();
|
||||
// }
|
||||
|
||||
throw Error('Cant create File at the specified path. The given URL contains a File. See "' + currentPath + '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
// // Reassemble the Segments like that:
|
||||
// //
|
||||
// // C:\\Test\\SubFolder\\AnotherFolder
|
||||
// // Split up:
|
||||
// // => fileParts = [C: , Test , SubFolder, AnotherFolder]
|
||||
// //
|
||||
// // Reassemble:
|
||||
// // 1) C:
|
||||
// // 2) C:\\Test
|
||||
// // 3) C:\\Test\\SubFolder
|
||||
// // 4) ...
|
||||
// //
|
||||
// // Everytime after an assembly Check whether the assembled
|
||||
// // path exists, otherwise create that folder and go on.
|
||||
|
||||
return path;
|
||||
// for (const [idx, folder] of pathParts.entries()) {
|
||||
// // Assemble the Path.
|
||||
// const currentPath = pathParts.slice(0, idx + 1).join(SPLITCHAR);
|
||||
// // Test and create the Folder
|
||||
// const urlExists = await _exists(currentPath);
|
||||
// // Test if the provided Path exists or not.
|
||||
// if (currentPath && !urlExists) {
|
||||
// await mkdir(currentPath);
|
||||
// } else if (currentPath && urlExists && (await _lstat(currentPath)).isFile()) {
|
||||
|
||||
// throw Error('Cant create File at the specified path. The given URL contains a File. See "' + currentPath + '"');
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return path;
|
||||
}
|
@ -2,11 +2,17 @@ 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 } from "./fileHelpers";
|
||||
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.
|
||||
@ -16,8 +22,10 @@ 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)
|
||||
@ -42,10 +50,12 @@ export async function generateOpenAPI(options: {
|
||||
outputSchema: string
|
||||
})[] = []
|
||||
|
||||
await createPath(join(options.outputDir));
|
||||
|
||||
// Firstly copy the nopeDispatcher
|
||||
await copyFile(
|
||||
join(__dirname, '..', '..', '..', 'lib', 'dispatcher', 'nopeDispatcher.ts'),
|
||||
join(options.tempDir, 'api', 'nopeDispatcher.ts')
|
||||
join(options.outputDir, 'nopeDispatcher.ts')
|
||||
);
|
||||
|
||||
for (const relevantClass of analysis) {
|
||||
@ -66,14 +76,15 @@ export async function generateOpenAPI(options: {
|
||||
customParameters: JSON.stringify(method.decoratorSettings.exportMethodToOpenAPI.parameters || [], undefined, 4),
|
||||
useCustomParameters: Array.isArray(method.decoratorSettings.exportMethodToOpenAPI.parameters),
|
||||
useDefaultParameters: method.params.length > 0,
|
||||
inputSchema: JSON.stringify(schemaMapping[relevantClass.className], undefined, 4),
|
||||
outputSchema: JSON.stringify(schemaMapping[relevantClass.className], undefined, 4)
|
||||
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'
|
||||
method: method.params.length > 0 ? 'POST' : 'GET',
|
||||
operationId: settings.className + method.name,
|
||||
},
|
||||
settings,
|
||||
method.decoratorSettings.exportMethodToOpenAPI as IExportMethodToOpenAPIParameters
|
||||
@ -81,7 +92,7 @@ export async function generateOpenAPI(options: {
|
||||
|
||||
methods.push(item);
|
||||
|
||||
const fileName = join(options.tempDir, 'api', item.baseUri, item.methodUri + '.ts');
|
||||
const fileName = join(options.outputDir, item.baseUri, item.methodUri + '.ts');
|
||||
|
||||
// Write down the Schema:
|
||||
await createFile(
|
||||
@ -90,6 +101,10 @@ export async function generateOpenAPI(options: {
|
||||
renderAPI(item)
|
||||
);
|
||||
|
||||
if (options.logger) {
|
||||
options.logger.info('Generated -> ' + fileName);
|
||||
}
|
||||
|
||||
// Function to Determine new project files.
|
||||
const project = new Project({
|
||||
tsConfigFilePath: options.tsConfigFilePath,
|
||||
|
@ -3,9 +3,11 @@ import * as handlebars from 'handlebars';
|
||||
import { join } from 'path';
|
||||
import { Project } from "ts-morph";
|
||||
import * as TJS from "typescript-json-schema";
|
||||
import { Logger } from 'winston';
|
||||
import { IJsonSchema } from "../../types/IJSONSchema";
|
||||
import { analyzeFiles, defaultClassFilter, defaultPropFilter } from "./analyzeTypescriptFiles";
|
||||
import { createFile, createPath } from "./fileHelpers";
|
||||
import { flattenSchema } from "./jsonSchemaMethods";
|
||||
|
||||
|
||||
/**
|
||||
@ -17,6 +19,7 @@ export async function generateSchemas(options: {
|
||||
tempDir: string,
|
||||
inputDir: string,
|
||||
tsConfigFilePath: string,
|
||||
logger?: Logger,
|
||||
}) {
|
||||
|
||||
// Create the Output dir (if it doenst exists)
|
||||
@ -75,16 +78,22 @@ export async function generateSchemas(options: {
|
||||
});
|
||||
|
||||
for (const file of files) {
|
||||
|
||||
// Generate the Path.
|
||||
const fileName = join(options.tempDir, 'classes', file.name + '.ts');
|
||||
await createFile(
|
||||
// Generate the Path.
|
||||
join(options.tempDir, 'classes', file.name + '.ts'),
|
||||
fileName,
|
||||
file.content
|
||||
);
|
||||
|
||||
if (options.logger) {
|
||||
options.logger.info('Generated -> ' + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
// After all files has been written => Generate the Schemas:
|
||||
const _settings: TJS.PartialArgs = {
|
||||
required: true
|
||||
required: true,
|
||||
};
|
||||
|
||||
// Options for the TJS.Compiler;
|
||||
@ -103,15 +112,25 @@ export async function generateSchemas(options: {
|
||||
|
||||
// We can either get the schema for one file and one type...
|
||||
const _schema = TJS.generateSchema(_program, "*", _settings);
|
||||
|
||||
// Flatten the Schema.
|
||||
const _flattendSchema = flattenSchema(_schema as any)
|
||||
|
||||
// Receive the Element
|
||||
schemaMapping[file.className] = JSON.parse(JSON.stringify(_schema)) as IJsonSchema;
|
||||
schemaMapping[file.className] = _flattendSchema;
|
||||
|
||||
// Generate the Path.
|
||||
const fileName = join(options.tempDir, 'schema', file.name + '.json')
|
||||
|
||||
// Write down the Schema:
|
||||
await createFile(
|
||||
// Generate the Path.
|
||||
join(options.tempDir, 'schema', file.name + '.json'),
|
||||
JSON.stringify(_schema, undefined, 4)
|
||||
fileName,
|
||||
JSON.stringify(_flattendSchema, undefined, 4)
|
||||
);
|
||||
|
||||
if (options.logger) {
|
||||
options.logger.info('Generated -> ' + fileName);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -2,6 +2,7 @@ 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 { analyzeFiles } from "./analyzeTypescriptFiles";
|
||||
import { createFile, createPath } from "./fileHelpers";
|
||||
|
||||
@ -14,11 +15,17 @@ export async function generateClientTemplate(options: {
|
||||
outputDir: string,
|
||||
inputDir: string,
|
||||
tsConfigFilePath: string,
|
||||
// A Logger.
|
||||
logger?: Logger
|
||||
}) {
|
||||
|
||||
// Create the Output dir (if it doenst exists)
|
||||
await createPath(options.outputDir);
|
||||
|
||||
if (options.logger) {
|
||||
options.logger.info('Templates will be stored in ' + options.outputDir);
|
||||
}
|
||||
|
||||
// Firstly copy the nopeDispatcher
|
||||
await copyFile(
|
||||
join(__dirname, '..', '..', '..', 'lib', 'dispatcher', 'nopeDispatcher.ts'),
|
||||
@ -62,13 +69,19 @@ export async function generateClientTemplate(options: {
|
||||
});
|
||||
|
||||
for (const file of files) {
|
||||
// Define the File Name:
|
||||
const fileName = join(options.outputDir, 'clients', file.name);
|
||||
|
||||
await createFile(
|
||||
// Generate the Path.
|
||||
join(options.outputDir, 'clients', file.name),
|
||||
file.content
|
||||
);
|
||||
|
||||
if (options.logger) {
|
||||
options.logger.info('Generated -> ' + fileName);
|
||||
}
|
||||
|
||||
// Function to Determine new project files.
|
||||
const project = new Project({
|
||||
tsConfigFilePath: options.tsConfigFilePath,
|
||||
|
76
lib/helpers/jsonMethods.ts
Normal file
76
lib/helpers/jsonMethods.ts
Normal file
@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Function to stringify an Object. This Function will stringify Functions as well.
|
||||
* @param obj The Object.
|
||||
*/
|
||||
export function stringifyWithFunctions(obj) {
|
||||
return JSON.stringify(obj, (key, value) => {
|
||||
if (typeof value === "function") {
|
||||
|
||||
let str: string = value.toString();
|
||||
|
||||
// Todo Parse Arrow-Functions Correctly!
|
||||
// Details here: https://zendev.com/2018/10/01/javascript-arrow-functions-how-why-when.html
|
||||
// Difference Cases For:
|
||||
// 1) (a, b) => a + b;
|
||||
// 2) array => array[0];
|
||||
// 3) (a, b) => (a + b);
|
||||
// 4) (name, description) => ({name: name, description: description})
|
||||
// ....
|
||||
|
||||
if (!str.startsWith('function') && !str.startsWith('(')) {
|
||||
const name = str.slice(0, str.indexOf('=>'));
|
||||
const func = str.slice(str.indexOf('=>(') + 3, str.length - 2)
|
||||
const adaptedFunc = 'function(' + name + '){ return ' + func + '; }';
|
||||
str = adaptedFunc;
|
||||
}
|
||||
|
||||
return "/Function(" + str + ")/";
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to parse a JSON String, in which methods should be available.
|
||||
* @param json A String containing the json Object
|
||||
*/
|
||||
export function parseWithFunctions(json: string) {
|
||||
return JSON.parse(json, (key, value) => {
|
||||
if (typeof value === "string" &&
|
||||
value.startsWith("/Function(") &&
|
||||
value.endsWith(")/")) {
|
||||
const _value = value.substring(10, value.length - 2);
|
||||
try {
|
||||
return eval("(" + _value + ")");
|
||||
} catch (e) {
|
||||
console.log('FAILED PARSING', value, _value)
|
||||
}
|
||||
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to stringify an Object. This Function is able to stringify Functions as well. Use the Flag withFunctions
|
||||
* @param obj The Object.
|
||||
* @param withFunctions Flag to Turn on / off the parsing of functions
|
||||
*/
|
||||
export function stringify(obj: any, withFunctions = false): string {
|
||||
if (withFunctions) {
|
||||
return stringifyWithFunctions(obj);
|
||||
}
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to parse a JSON String. This Function is able to parse Functions as well. Use the Flag withFunctions
|
||||
* @param json A String containing the json Object
|
||||
* @param withFunctions Flag to Turn on / off the parsing of functions
|
||||
*/
|
||||
export function parse(json: string, withFunctions = false): any {
|
||||
if (withFunctions) {
|
||||
return parseWithFunctions(json);
|
||||
}
|
||||
return JSON.parse(json);
|
||||
}
|
55
lib/helpers/jsonSchemaMethods.ts
Normal file
55
lib/helpers/jsonSchemaMethods.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { IJsonSchema } from "../../types/IJSONSchema";
|
||||
import { flattenObject, rgetattr, rsetattr, SPLITCHAR } from "./objectMethods";
|
||||
|
||||
/**
|
||||
* Function to Flatten a JSON-Schema.
|
||||
* @param schema
|
||||
*/
|
||||
export function flattenSchema(schema: IJsonSchema) {
|
||||
|
||||
let counter = 10000;
|
||||
let flattenSchema = flattenObject(schema);
|
||||
|
||||
const getRefKeys = (flattenSchema: Map<string, any>) => {
|
||||
const relevantKeys: Array<{ schemaPath: string, searchPath: string }> = []
|
||||
|
||||
for (const [key, value] of flattenSchema) {
|
||||
if (key.endsWith('$ref')) {
|
||||
relevantKeys.push({
|
||||
schemaPath: key,
|
||||
searchPath: value.replace('#/', '').replace('/', SPLITCHAR)
|
||||
})
|
||||
}
|
||||
}
|
||||
return relevantKeys;
|
||||
}
|
||||
|
||||
let refs = getRefKeys(flattenSchema);
|
||||
while (refs.length > 0) {
|
||||
|
||||
counter--;
|
||||
|
||||
if (counter === 0) {
|
||||
throw Error('Max amount of Recursions performed')
|
||||
}
|
||||
|
||||
for (const ref of refs) {
|
||||
const subSchema = rgetattr(schema, ref.searchPath, null, '.');
|
||||
rsetattr(schema, ref.schemaPath.replace('.$ref', ''), subSchema)
|
||||
}
|
||||
|
||||
flattenSchema = flattenObject(schema);
|
||||
refs = getRefKeys(flattenSchema);
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to get a Schemas Definition
|
||||
* @param schema the JSON-Schema
|
||||
* @param reference the path of the relevant definition.
|
||||
*/
|
||||
export function schemaGetDefinition(schema: IJsonSchema, reference: string) {
|
||||
return rgetattr(schema, reference.replace('#/', ''), null, '/');
|
||||
}
|
366
lib/helpers/objectMethods.ts
Normal file
366
lib/helpers/objectMethods.ts
Normal file
@ -0,0 +1,366 @@
|
||||
export const SPLITCHAR = '.';
|
||||
|
||||
const _sentinel = new Object();
|
||||
|
||||
/**
|
||||
* Function to recurvely get an Attribute of the Object.
|
||||
*
|
||||
* @export
|
||||
* @param {*} _data
|
||||
* @param {string} _path
|
||||
* @param {*} [_default=_sentinel]
|
||||
* @returns {*}
|
||||
*/
|
||||
export function rgetattr(_data: any, _path: string, _default: any = _sentinel, _SPLITCHAR: string = SPLITCHAR): any | null {
|
||||
// Extract the Path
|
||||
let _obj = _data;
|
||||
|
||||
if (_path.length > 0) {
|
||||
/** Check if there is a Substring available perform the normal method */
|
||||
if (_path.indexOf(_SPLITCHAR) !== -1) {
|
||||
for (const attr of _path.split(_SPLITCHAR)) {
|
||||
/** Access a Map */
|
||||
if (_obj instanceof Map) {
|
||||
_obj = _obj.get(attr);
|
||||
} else {
|
||||
/** Array or default Object */
|
||||
_obj = _obj[attr];
|
||||
}
|
||||
|
||||
if ((_obj == null) && (_default === _sentinel)) {
|
||||
return null;
|
||||
} else if (_obj == null) {
|
||||
return _default;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/** Otherwise just return the Element */
|
||||
return _obj[_path];
|
||||
}
|
||||
}
|
||||
return _obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to Set recursely a Attribute of an Object
|
||||
*
|
||||
* @export
|
||||
* @param {*} _data The Object, where the data should be stored
|
||||
* @param {string} _path The Path of the Attribute. All are seprated by a '.'! For Instance 'a.b.0.a.c'
|
||||
* @param {*} _value The Value which should be Stored in the Attribute.
|
||||
*/
|
||||
export function rsetattr(_data: any, _path: string, _value: any, _SPLITCHAR: string = SPLITCHAR): void {
|
||||
let _obj = _data;
|
||||
|
||||
|
||||
const _ptrs = _path.split(_SPLITCHAR);
|
||||
|
||||
_ptrs.slice(0, -1).forEach(function (attr: string, idx: number) {
|
||||
// Adapt the Object by going through a loop
|
||||
let _sub = _obj[attr];
|
||||
|
||||
if (_sub === undefined || _sub === null) {
|
||||
// _obj is an Array and it doesnt contain the index
|
||||
|
||||
// Extract the Next Element:
|
||||
const _next = _ptrs[idx + 1];
|
||||
|
||||
const _next_is_int = isInt(_next);
|
||||
|
||||
if (Array.isArray(_obj)) {
|
||||
if (_next_is_int) {
|
||||
_obj[attr] = new Array<any>();
|
||||
} else {
|
||||
_obj[attr] = {};
|
||||
}
|
||||
} else {
|
||||
if (_next_is_int) {
|
||||
_obj[attr] = [];
|
||||
} else {
|
||||
_obj[attr] = {};
|
||||
}
|
||||
}
|
||||
_sub = _obj[attr];
|
||||
|
||||
}
|
||||
|
||||
_obj = _sub;
|
||||
});
|
||||
_obj[_ptrs[_ptrs.length - 1]] = _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Value is an Integer
|
||||
*
|
||||
* @export
|
||||
* @param {*} value Value to be checked
|
||||
* @returns {boolean} Result
|
||||
*/
|
||||
export function isInt(value: any): boolean {
|
||||
return (parseInt(value) === value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Value is a Float
|
||||
*
|
||||
* @export
|
||||
* @param {*} value Value to be checked
|
||||
* @returns {boolean} Result
|
||||
*/
|
||||
export function isFloat(value: any): boolean {
|
||||
return (!isNaN(Number(value)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copys the Object. Creates a Deep-Copy
|
||||
* of the Function
|
||||
*
|
||||
* @export
|
||||
* @param {*} value The value which should be copied
|
||||
* @returns {*} A Copy of the Value
|
||||
*/
|
||||
export function copy(value: any): any {
|
||||
// TODO RING
|
||||
// const _copy = {};
|
||||
|
||||
// /** Perform a Recursevly Foreach an Set an Attribute. */
|
||||
// recursiveForEach(value, '', (path: string, _data: any) => {
|
||||
// rsetattr(_copy, path, _data);
|
||||
// });
|
||||
|
||||
// return _copy;
|
||||
return JSON.parse(JSON.stringify(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Converts a Object to a Map.
|
||||
*
|
||||
* @export
|
||||
* @param {*} _obj The Object which should be converted.
|
||||
* @returns {Map<string,any>}
|
||||
*/
|
||||
export function objectToMap(_obj: any): Map<string, any> {
|
||||
/** Define the Returntype */
|
||||
const _ret = new Map<string, any>();
|
||||
|
||||
/** Iterate through all properties of the Object */
|
||||
for (const _prop of Object.getOwnPropertyNames(_obj)) {
|
||||
/** If isnt a function it could be added */
|
||||
if (typeof _obj !== 'function') {
|
||||
_ret.set(_prop, _obj[_prop]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the Result */
|
||||
return _ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Value is an Object
|
||||
*
|
||||
* @export
|
||||
* @param {*} value Data to Test
|
||||
* @returns {boolean} Flag showing whether the Presented Data is an Object
|
||||
*/
|
||||
export function isObject(value: any): boolean {
|
||||
/** Verify whether the value contains some data. */
|
||||
if (value) {
|
||||
if ((typeof value === 'object') && !Array.isArray(value)) {
|
||||
return Object.keys(value).length > 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the Value is an Object
|
||||
*
|
||||
* @export
|
||||
* @param {*} value Data to Test
|
||||
* @returns {boolean} Flag showing whether the Presented Data is an Object
|
||||
*/
|
||||
export function isObjectOrArray(value: any): boolean {
|
||||
/** Verify whether the value contains some data. */
|
||||
return isObject(value) || Array.isArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattens an Object to a Map.
|
||||
*
|
||||
* For Instance:
|
||||
*
|
||||
* data = {a : { b : { c : 1, d: "hallo"}}}
|
||||
*
|
||||
* // Normal Call
|
||||
* res = flatteObject(data,'')
|
||||
* => res = {"a.b.c":1,"a.b.d":"hallo"}
|
||||
*
|
||||
* // With a Selected prefix 'additional.name'
|
||||
* res = flatteObject(data,'additional.name')
|
||||
* => res = {"additional.name.a.b.c":1,"additional.name.a.b.d":"hallo"}
|
||||
*
|
||||
* @export
|
||||
* @param {*} data The Data that should be converted
|
||||
* @param {string} [prefix=''] An additional prefix.
|
||||
* @returns {Map<string, any>} The flatten Object
|
||||
*/
|
||||
export function flattenObject(data: any, prefix: string = '', splitchar: string = SPLITCHAR): Map<string, any> {
|
||||
|
||||
const _ret = new Map<string, any>();
|
||||
|
||||
if (isObject(data) || Array.isArray(data)) {
|
||||
recursiveForEach(data, prefix, (path, _data) => {
|
||||
_ret.set(path, _data);
|
||||
}, splitchar, true);
|
||||
}
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function which is executed on each
|
||||
*
|
||||
* @export
|
||||
* @param {*} obj
|
||||
* @param {string} [prefix='']
|
||||
* @param {(path: string, data: any) => void} dataCallback
|
||||
* @returns {*}
|
||||
*/
|
||||
export function recursiveForEach(obj: any, prefix: string = '', dataCallback: (path: string, data: any, parent?: string, level?: number) => void, _SPLITCHAR: string = SPLITCHAR, _callOnlyOnValues = true, _parent: string = '', _level = 0): any {
|
||||
/** Create an Array containing all Keys. */
|
||||
let keys = Array<string>();
|
||||
|
||||
/** Extract Keys of the Object, only if it isnt a string */
|
||||
if (typeof obj !== 'string' && typeof obj !== 'function') {
|
||||
keys = Object.getOwnPropertyNames(obj);
|
||||
if (Array.isArray(obj)) {
|
||||
keys.splice(keys.indexOf('length'), 1);
|
||||
}
|
||||
}
|
||||
|
||||
let called = false;
|
||||
|
||||
if (!_callOnlyOnValues) {
|
||||
// Store the Element !
|
||||
dataCallback(prefix, obj, _parent, _level);
|
||||
called = true;
|
||||
}
|
||||
|
||||
// If there are Keys => It is a List or a Default Object
|
||||
if (keys.length > 0) {
|
||||
for (const _key of keys) {
|
||||
/** Var containing the Name */
|
||||
const _str = (prefix === '') ? _key : prefix + _SPLITCHAR + _key;
|
||||
|
||||
if (obj[_key] != null) {
|
||||
if (typeof obj[_key].toJSON === 'function') {
|
||||
const data = obj[_key].toJSON();
|
||||
/** Recursive call the Function */
|
||||
recursiveForEach(data, _str, dataCallback, _SPLITCHAR, _callOnlyOnValues, prefix, _level + 1);
|
||||
} else {
|
||||
/** Recursive call the Function */
|
||||
recursiveForEach(obj[_key], _str, dataCallback, _SPLITCHAR, _callOnlyOnValues, prefix, _level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if (!called) {
|
||||
// Store the Element !
|
||||
dataCallback(prefix, obj, prefix, _level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the used Types of an Object
|
||||
*
|
||||
* @export
|
||||
* @param {*} data The Data considered
|
||||
* @param {string} [prefix='']
|
||||
* @returns {Map<string, string>}
|
||||
*/
|
||||
export function flattenObjectType(data: any, prefix: string = ''): Map<string, string> {
|
||||
const _ret = new Map<string, string>();
|
||||
|
||||
if (isObject(data)) {
|
||||
recursiveForEach(data, prefix, (path, _data) => {
|
||||
_ret.set(path, typeof _data);
|
||||
});
|
||||
}
|
||||
return _ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deflattens an Dict Based Object.
|
||||
*
|
||||
* @export
|
||||
* @param {Map<string, any>} _flattenObject
|
||||
* @returns {*}
|
||||
*/
|
||||
export function deflattenObject(_flattenObject: Map<string, any>): any {
|
||||
const _ret = {};
|
||||
|
||||
_flattenObject.forEach((_val: any, _key: string) => {
|
||||
rsetattr(_ret, _key, _val);
|
||||
});
|
||||
|
||||
return _ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function for deeply assigning
|
||||
*
|
||||
* @export
|
||||
* @param {*} target
|
||||
* @param {*} source
|
||||
* @returns
|
||||
*/
|
||||
export function deepAssign(target: any, source: any) {
|
||||
const flattend = flattenObject(source);
|
||||
|
||||
for (const [path, value] of flattend.entries()) {
|
||||
rsetattr(target, path, value);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
export function deepClone<T>(obj: T) {
|
||||
let clone: any = Object.assign({}, obj);
|
||||
Object.keys(clone).forEach(
|
||||
key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
|
||||
);
|
||||
return (Array.isArray(obj) && obj.length
|
||||
? (clone.length = obj.length) && Array.from(clone)
|
||||
: Array.isArray(obj)
|
||||
? Array.from(obj)
|
||||
: clone) as T;
|
||||
}
|
||||
|
||||
// export const deepEqual: (a: any, b: any) => boolean = require('deep-equal');
|
||||
|
||||
/**
|
||||
* Function to adapt the Object and only return a specific amount of elements.
|
||||
* @param obj The Object itself
|
||||
* @param properties a list of properties/pathes to keep
|
||||
*/
|
||||
export function keepPropertiesOfObject(obj: any, properties: { [index: string]: () => any }) {
|
||||
if (isObject(obj)) {
|
||||
const ret: any = {};
|
||||
|
||||
const defaultObj = { error: true };
|
||||
|
||||
// Iterate over the Properties, get the content of the path, clone it an put it to the
|
||||
// provided path
|
||||
Object.getOwnPropertyNames(properties).map(path => {
|
||||
const value = rgetattr(obj, path, defaultObj);
|
||||
rsetattr(ret, path, value !== defaultObj ? (typeof value === 'object' ? deepClone(value) : value) : properties[path]());
|
||||
});
|
||||
|
||||
// Return the Object
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Wrong Datatype provided.
|
||||
throw TypeError('Function can only create Objects');
|
||||
}
|
52
lib/helpers/stringMethods.ts
Normal file
52
lib/helpers/stringMethods.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Replaces all Chars in a String
|
||||
* @param str base string
|
||||
* @param value the value which should be replaced
|
||||
* @param replacement the value which is used as replacement
|
||||
*/
|
||||
export function replaceAll(str: string, value: string, replacement: string): string {
|
||||
return str.split(value).join(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to Pad a String.
|
||||
* @param num
|
||||
* @param size
|
||||
* @param maxLength
|
||||
*/
|
||||
export function padString(num: number, size: number, maxLength = false) {
|
||||
|
||||
let _size = size;
|
||||
if (typeof maxLength === 'boolean' && maxLength) {
|
||||
_size = Math.ceil(Math.log10(size));
|
||||
}
|
||||
|
||||
let s = num + "";
|
||||
while (s.length < _size) s = "0" + s;
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a String in the String
|
||||
* @param str base string
|
||||
* @param index index where the content should be inserted
|
||||
* @param content the content to insert
|
||||
*/
|
||||
export function insert(str: string, index: number, content: string): string {
|
||||
if (index > 0) {
|
||||
return str.substring(0, index) + content + str.substring(index, str.length);
|
||||
} else {
|
||||
return content + str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to Camelize a String
|
||||
* @param str The String,
|
||||
* @param char A, used to determine "new words"
|
||||
*/
|
||||
export function camelize(str: string, char = '_') {
|
||||
return replaceAll(str, char, ' ').replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
|
||||
return index == 0 ? word.toLowerCase() : word.toUpperCase();
|
||||
}).replace(/\s+/g, '');
|
||||
}
|
32
lib/logger/getLogger.ts
Normal file
32
lib/logger/getLogger.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import { createLogger, format, transports } from 'winston';
|
||||
|
||||
export type LoggerLevel = 'error' | 'warn' | 'info' | 'http' | 'verbose' | 'debug' | 'silly'
|
||||
|
||||
export function getLogger(level: LoggerLevel) {
|
||||
const readableFormat = format.printf(({ level, message, timestamp }) => {
|
||||
return `[${timestamp} ${level}]: ${message}`;
|
||||
});
|
||||
|
||||
// Define a Logger
|
||||
const logger = createLogger({
|
||||
level,
|
||||
format: format.combine(
|
||||
format.timestamp({
|
||||
format: 'YYYY-MM-DD HH:mm:ss-ms'
|
||||
}),
|
||||
format.errors({ stack: true }),
|
||||
readableFormat
|
||||
),
|
||||
exitOnError: false,
|
||||
transports: [
|
||||
new (transports.Console)({
|
||||
format: format.combine(
|
||||
format.colorize(),
|
||||
readableFormat
|
||||
)
|
||||
}),
|
||||
]
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
@ -26,8 +26,7 @@ export default function (_dispatcher: nopeDispatcher) {
|
||||
// Define the apiDoc for this specific Funtion
|
||||
POST.apiDoc = {
|
||||
{{#if methodDescription}}summary: '{{methodDescription}}',{{/if}}
|
||||
{{#if operationId}}operationId: '{{operationId}}',{{/if}}{{#if operationId}}operationId: '{{operationId}}',{{/if}}
|
||||
operationId: '{{operationId}}',
|
||||
{{#if operationId}}operationId: '{{operationId}}'{{/if}},
|
||||
parameters: [
|
||||
{{#if useDefaultParameters}}
|
||||
{
|
||||
@ -46,12 +45,14 @@ export default function (_dispatcher: nopeDispatcher) {
|
||||
{{#if hasReturnType}}
|
||||
200: {
|
||||
{{#if resultDescription}}description: '{{resultDescription}}',{{/if}}
|
||||
{{#unless resultDescription}}description: 'Not Provided', {{/unless}}
|
||||
schema: {{{outputSchema}}}
|
||||
},
|
||||
{{/if}}
|
||||
{{#unless hasReturnType}}
|
||||
200: {
|
||||
{{#if resultDescription}}description: '{{resultDescription}}',{{/if}}
|
||||
{{#unless resultDescription}}description: 'Not Provided', {{/unless}}
|
||||
},
|
||||
{{/unless}}
|
||||
default: {
|
||||
|
@ -16,7 +16,7 @@ export interface {{name}}Input {
|
||||
{{name}}{{#if isOptional}}?{{/if}}: {{{originalCode}}}
|
||||
{{/if}}
|
||||
{{#unless isBaseType}}
|
||||
{{{simplifiedType}}}
|
||||
{{name}}{{#if isOptional}}?{{/if}}: {{{simplifiedType}}}
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
}
|
||||
|
16
nopeconfig.json
Normal file
16
nopeconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"dispatcher": {
|
||||
"inputDir": "./test/*.ts",
|
||||
"outputDir": "./pages/backend",
|
||||
"pathToTemplate": "./lib/templates/clientInterface.handlebars",
|
||||
"tsConfigFilePath": "./tsconfigBackend.json"
|
||||
},
|
||||
"openapi": {
|
||||
"inputDir": "./test/*.ts",
|
||||
"outputDir": "./api/backend",
|
||||
"tempDir": "./temp/",
|
||||
"pathToSchemaTemplate": "./lib/templates/schema.handlebars",
|
||||
"pathToApiTemplate": "./lib/templates/openApiSchema.handlebars",
|
||||
"tsConfigFilePath": "./tsconfigBackend.json"
|
||||
}
|
||||
}
|
944
package-lock.json
generated
944
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -40,8 +40,10 @@
|
||||
"rxjs": "^6.6.2",
|
||||
"socket.io": "^2.3.0",
|
||||
"socket.io-client": "^2.3.0",
|
||||
"swagger-ui-react": "^3.32.4",
|
||||
"ts-morph": "^7.3.0",
|
||||
"typescript-json-schema": "^0.43.0"
|
||||
"typescript-json-schema": "^0.43.0",
|
||||
"winston": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@openapitools/openapi-generator-cli": "^1.0.15-4.3.1",
|
||||
|
2
pages/.gitignore
vendored
Normal file
2
pages/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Logs
|
||||
backend
|
@ -1,4 +1,5 @@
|
||||
import 'bootstrap/dist/css/bootstrap.min.css';
|
||||
import "swagger-ui-react/swagger-ui.css";
|
||||
|
||||
export default function App({ Component, pageProps }) {
|
||||
return <Component {...pageProps} />
|
||||
|
14
pages/docs.tsx
Normal file
14
pages/docs.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import SwaggerUI from "swagger-ui-react";
|
||||
import { LocalNavbar } from './ui/navbar';
|
||||
|
||||
/**
|
||||
* Default Docs.
|
||||
*/
|
||||
export default function Docs() {
|
||||
return (
|
||||
<>
|
||||
<LocalNavbar></LocalNavbar>
|
||||
<SwaggerUI url="http://localhost:3001/api/api-docs" />
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,6 +1,15 @@
|
||||
export default function Home(){
|
||||
import Head from 'next/head';
|
||||
import { LocalNavbar } from './ui/navbar';
|
||||
|
||||
export default function Home() {
|
||||
|
||||
return (
|
||||
<h1>Welcome</h1>
|
||||
<>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<LocalNavbar></LocalNavbar>
|
||||
</>
|
||||
)
|
||||
}
|
24
pages/ui/navbar.tsx
Normal file
24
pages/ui/navbar.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
import { Nav, Navbar, NavDropdown } from "react-bootstrap";
|
||||
|
||||
export function LocalNavbar() {
|
||||
return (
|
||||
<Navbar collapseOnSelect sticky="top" expand="lg" bg="dark" variant="dark">
|
||||
<Navbar.Brand href="/">nopeBackend</Navbar.Brand>
|
||||
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
|
||||
<Navbar.Collapse id="responsive-navbar-nav">
|
||||
<Nav className="mr-auto">
|
||||
<Nav.Link href="/docs">Docs</Nav.Link>
|
||||
{/* <Nav.Link href="#features">Features</Nav.Link> */}
|
||||
<NavDropdown title="Modules" id="collasible-nav-dropdown">
|
||||
{/* Iterate over the deteced Modules. */}
|
||||
<NavDropdown.Item href="#action/3.1">Action</NavDropdown.Item>
|
||||
</NavDropdown>
|
||||
</Nav>
|
||||
{/* Navigation on the right side */}
|
||||
{/* <Nav>
|
||||
|
||||
</Nav> */}
|
||||
</Navbar.Collapse>
|
||||
</Navbar>
|
||||
)
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
import { getLogger } from "../lib/logger/getLogger";
|
||||
import { startBackend } from "./startBackend";
|
||||
|
||||
startBackend({port: 3001});
|
||||
startBackend({ port: 3001, logger: getLogger('debug') });
|
@ -1,14 +1,19 @@
|
||||
import { apiDoc } from '../specs/apiDoc';
|
||||
import * as express from "express";
|
||||
import * as bodyParser from "body-parser";
|
||||
import * as cors from 'cors';
|
||||
import * as express from "express";
|
||||
import { initialize } from "express-openapi";
|
||||
import { getBackendAccesors } from './getBackendAccessors';
|
||||
import { assignIn } from 'lodash';
|
||||
import { Logger } from 'winston';
|
||||
import { EventLayer } from "../lib/communication/eventLayer";
|
||||
import { getDispatcher } from "../lib/dispatcher/nopeDispatcher";
|
||||
import { apiDoc } from '../specs/apiDoc';
|
||||
import { getBackendAccesors } from './getBackendAccessors';
|
||||
|
||||
export function startBackend(options: {
|
||||
port?: number,
|
||||
basePath?: string,
|
||||
}= {}) {
|
||||
logger?: Logger,
|
||||
} = {}) {
|
||||
const app: express.Application = (express as any)();
|
||||
|
||||
// Define the Default Options
|
||||
@ -20,24 +25,34 @@ export function startBackend(options: {
|
||||
const opts = assignIn(defaults, options);
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.use(cors());
|
||||
|
||||
initialize({
|
||||
apiDoc: apiDoc('Test API', '0.0.1', opts.basePath),
|
||||
apiDoc: apiDoc('Backend API', '1.0.0', opts.basePath),
|
||||
app,
|
||||
paths: './dist/api',
|
||||
routesGlob: '**/*.{ts,js}',
|
||||
routesIndexFileRegExp: /(?:index)?\.[tj]s$/
|
||||
routesIndexFileRegExp: /(?:index)?\.[tj]s$/,
|
||||
dependencies: {
|
||||
_dispatcher: getDispatcher('backend', new EventLayer())
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
app.use(((err, req, res, next) => {
|
||||
res.status(err.status).json(err);
|
||||
}) as express.ErrorRequestHandler);
|
||||
|
||||
|
||||
const server = app.listen(opts.port);
|
||||
|
||||
const accessor = getBackendAccesors(app);
|
||||
|
||||
return {
|
||||
if (options.logger) {
|
||||
options.logger.info('Server Running on http://localhost:' + opts.port.toString() + opts.basePath);
|
||||
options.logger.info('API Documentation available on http://localhost:' + opts.port.toString() + opts.basePath + '/api-docs');
|
||||
options.logger.debug('Checkout http://localhost:3000/docs')
|
||||
}
|
||||
|
||||
return {
|
||||
app,
|
||||
// Accessor for the Server
|
||||
accessor,
|
||||
|
@ -1,22 +1,52 @@
|
||||
import { createLogger, format, transports } from 'winston';
|
||||
import { generateOpenAPI } from "../lib/helpers/generateOpenAPI";
|
||||
import { generateClientTemplate } from "../lib/helpers/generateTemplate";
|
||||
|
||||
const main = async function () {
|
||||
|
||||
const readableFormat = format.printf(({ level, message, label, timestamp }) => {
|
||||
return `${timestamp} ${level}: ${message}`;
|
||||
});
|
||||
|
||||
let logger = createLogger({
|
||||
level: 'info',
|
||||
format: format.combine(
|
||||
format.timestamp({
|
||||
format: 'YYYY-MM-DD HH:mm:ss'
|
||||
}),
|
||||
format.errors({ stack: true }),
|
||||
readableFormat
|
||||
),
|
||||
exitOnError: false,
|
||||
transports: [
|
||||
new (transports.Console)({
|
||||
format: format.combine(
|
||||
format.colorize(),
|
||||
readableFormat
|
||||
)
|
||||
}),
|
||||
]
|
||||
});
|
||||
|
||||
logger.info('Creating Dispatched API');
|
||||
|
||||
await generateClientTemplate({
|
||||
inputDir: './test/*.ts',
|
||||
outputDir: './dist/generated',
|
||||
pathToTemplate: './lib/templates/clientInterface.handlebars',
|
||||
tsConfigFilePath: './tsconfigBackend.json'
|
||||
tsConfigFilePath: './tsconfigBackend.json',
|
||||
logger
|
||||
});
|
||||
|
||||
console.log('Created Files.');
|
||||
logger.info('Creating Open-API');
|
||||
|
||||
await generateOpenAPI({
|
||||
inputDir: './test/*.ts',
|
||||
tempDir: './temp',
|
||||
pathToSchemaTemplate: './lib/templates/schema.handlebars',
|
||||
pathToApiTemplate: './lib/templates/openApiSchema.handlebars',
|
||||
tsConfigFilePath: './tsconfigBackend.json'
|
||||
tsConfigFilePath: './tsconfigBackend.json',
|
||||
logger
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,13 @@ export class CLWithInterface {
|
||||
num: 0
|
||||
});
|
||||
|
||||
@exportMethodToOpenAPI({})
|
||||
@exportMethodToDispatcher({
|
||||
url: 'exportedFunction'
|
||||
})
|
||||
async exportedFunctionShouldBeHosted(/* COMMENT */ a: IF00, b?: IF01) {
|
||||
return a + b
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
@ -49,12 +56,7 @@ export class CLWithInterface {
|
||||
return await operator(a, b);
|
||||
}
|
||||
|
||||
@exportMethodToOpenAPI({})
|
||||
@exportMethodToDispatcher({
|
||||
url: 'exportedFunction'
|
||||
})
|
||||
async exportedFunctionShouldBeHosted(/* COMMENT */ a: number, b?: number) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,5 @@
|
||||
export type IJsonSchemaBaseTypes = 'string' | 'number' | 'integer' | 'object' | 'array' | 'boolean' | 'null';
|
||||
export type IJsonSchemaTypes = IJsonSchemaBaseTypes | Array<IJsonSchemaBaseTypes>
|
||||
export type IJsonSchemaTypes = IJsonSchemaBaseTypes | Array<IJsonSchemaBaseTypes> | { $ref: string }
|
||||
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user