Skip to main content
Glama
generateModelCode.ts8.77 kB
/** * Generate Model Code Service * Generates TypeScript code for a model from the Swagger definition */ import fs from 'fs'; import path from 'path'; import logger from '../utils/logger.js'; // Interface for the function parameters export interface GenerateModelCodeParams { modelName: string; swaggerFilePath: string; // Required path to the Swagger file } /** * Generates TypeScript code for a model from the Swagger definition * @param params Object containing the model name and swagger file path * @returns TypeScript code for the model */ async function generateModelCode(params: GenerateModelCodeParams): Promise<string> { try { const { modelName, swaggerFilePath } = params; if (!swaggerFilePath) { throw new Error('Swagger file path is required'); } if (!fs.existsSync(swaggerFilePath)) { throw new Error(`Swagger file not found at ${swaggerFilePath}`); } // Read the Swagger definition file logger.info(`Reading Swagger definition from ${swaggerFilePath}`); const swaggerContent = fs.readFileSync(swaggerFilePath, 'utf8'); const swaggerDefinition = JSON.parse(swaggerContent); // Find the model schema let modelSchema = null; // Check if it's OpenAPI 3.0.x if (swaggerDefinition.openapi && swaggerDefinition.openapi.startsWith('3.')) { const schemas = swaggerDefinition.components?.schemas || {}; modelSchema = schemas[modelName]; } // Check if it's Swagger 2.0 else if (swaggerDefinition.swagger && swaggerDefinition.swagger.startsWith('2.')) { const definitions = swaggerDefinition.definitions || {}; modelSchema = definitions[modelName]; } if (!modelSchema) { throw new Error(`Model '${modelName}' not found in Swagger definition`); } // Generate TypeScript code const typeScriptCode = generateTypeScriptInterface(modelName, modelSchema, swaggerDefinition); return typeScriptCode; } catch (error: any) { logger.error(`Error generating model code: ${error.message}`); throw error; } } /** * Generates a TypeScript interface from a Swagger schema */ function generateTypeScriptInterface( modelName: string, schema: any, swaggerDefinition: any, indentLevel: number = 0 ): string { const indent = ' '.repeat(indentLevel); let result = ''; // Add description as JSDoc comment if available if (schema.description) { result += `${indent}/**\n`; result += `${indent} * ${schema.description}\n`; result += `${indent} */\n`; } // Start interface definition result += `${indent}export interface ${formatInterfaceName(modelName)} {\n`; // Process properties if (schema.properties) { for (const propName in schema.properties) { const property = schema.properties[propName]; // Add property description as JSDoc comment if available if (property.description) { result += `${indent} /**\n`; result += `${indent} * ${property.description}\n`; result += `${indent} */\n`; } // Determine if property is required const isRequired = schema.required && schema.required.includes(propName); const optionalMarker = isRequired ? '' : '?'; // Add property with type result += `${indent} ${propName}${optionalMarker}: ${getTypeScriptType(property, swaggerDefinition)};\n`; } } // Handle allOf, anyOf, oneOf ['allOf', 'anyOf', 'oneOf'].forEach(key => { if (Array.isArray(schema[key])) { // For allOf, we extend interfaces if (key === 'allOf') { schema[key].forEach((subSchema: any) => { if (subSchema.$ref) { const refModelName = getRefModelName(subSchema.$ref); result = result.replace( `export interface ${formatInterfaceName(modelName)} {`, `export interface ${formatInterfaceName(modelName)} extends ${formatInterfaceName(refModelName)} {` ); } else if (subSchema.properties) { // Inline properties from allOf for (const propName in subSchema.properties) { const property = subSchema.properties[propName]; // Add property description as JSDoc comment if available if (property.description) { result += `${indent} /**\n`; result += `${indent} * ${property.description}\n`; result += `${indent} */\n`; } // Determine if property is required const isRequired = subSchema.required && subSchema.required.includes(propName); const optionalMarker = isRequired ? '' : '?'; // Add property with type result += `${indent} ${propName}${optionalMarker}: ${getTypeScriptType(property, swaggerDefinition)};\n`; } } }); } // For anyOf and oneOf, we use union types else { // This is a simplified approach - for complex schemas, a more sophisticated solution would be needed const unionTypes = schema[key].map((subSchema: any) => { if (subSchema.$ref) { const refModelName = getRefModelName(subSchema.$ref); return formatInterfaceName(refModelName); } else { return 'any'; // Simplified for complex inline schemas } }).join(' | '); result += `${indent} // ${key} union type\n`; result += `${indent} value: ${unionTypes};\n`; } } }); // Close interface definition result += `${indent}}\n`; // Process nested types const nestedTypes: string[] = []; if (schema.properties) { for (const propName in schema.properties) { const property = schema.properties[propName]; const nestedType = generateNestedTypes(property, swaggerDefinition, indentLevel); if (nestedType) { nestedTypes.push(nestedType); } } } // Add nested types after the main interface if (nestedTypes.length > 0) { result += '\n' + nestedTypes.join('\n'); } return result; } /** * Generates nested types for complex properties */ function generateNestedTypes( schema: any, swaggerDefinition: any, indentLevel: number = 0 ): string | null { // Handle inline object definitions if (schema.type === 'object' && schema.properties && !schema.$ref) { const typeName = 'InlineType'; // This would need a more sophisticated naming strategy in a real implementation return generateTypeScriptInterface(typeName, schema, swaggerDefinition, indentLevel); } // Handle inline array item definitions if (schema.type === 'array' && schema.items && schema.items.type === 'object' && schema.items.properties && !schema.items.$ref) { const typeName = 'InlineArrayItem'; // This would need a more sophisticated naming strategy in a real implementation return generateTypeScriptInterface(typeName, schema.items, swaggerDefinition, indentLevel); } return null; } /** * Converts a Swagger type to a TypeScript type */ function getTypeScriptType(schema: any, swaggerDefinition: any): string { if (schema.$ref) { const refModelName = getRefModelName(schema.$ref); return formatInterfaceName(refModelName); } switch (schema.type) { case 'string': if (schema.enum) { // For enums, create a union of string literals return schema.enum.map((value: string) => `'${value}'`).join(' | '); } return 'string'; case 'integer': case 'number': return 'number'; case 'boolean': return 'boolean'; case 'array': if (schema.items) { return `${getTypeScriptType(schema.items, swaggerDefinition)}[]`; } return 'any[]'; case 'object': if (schema.additionalProperties) { // For dictionaries/maps return `Record<string, ${getTypeScriptType(schema.additionalProperties, swaggerDefinition)}>`; } return 'Record<string, any>'; default: return 'any'; } } /** * Extracts the model name from a JSON reference */ function getRefModelName(ref: string): string { const refParts = ref.split('/'); return refParts[refParts.length - 1]; } /** * Formats a model name as a valid TypeScript interface name */ function formatInterfaceName(name: string): string { // Remove special characters and ensure it starts with a letter let formattedName = name.replace(/[^\w]/g, ''); // Ensure the name starts with a capital letter formattedName = formattedName.charAt(0).toUpperCase() + formattedName.slice(1); return formattedName; } export default generateModelCode;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Vizioz/Swagger-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server