// Copyright (c) 2026 Gordon Bolton. MIT License. /** * Simple OpenAPI 3.0 to RAML 1.0 converter. * This is a basic converter that handles the essential parts of the sofarr API. * For a production system, you'd want a more sophisticated converter. */ const fs = require('fs'); const path = require('path'); const INPUT_FILE = path.join(process.cwd(), 'dist/openapi-30.json'); const OUTPUT_FILE = path.join(process.cwd(), 'dist/api.raml'); function convertToRaml(spec) { const lines = []; // RAML header lines.push('#%RAML 1.0'); lines.push(''); // Title and version lines.push(`title: ${spec.info.title}`); if (spec.info.version) { lines.push(`version: ${spec.info.version}`); } if (spec.info.description) { lines.push(`description: |`); spec.info.description.split('\n').forEach(line => { lines.push(` ${line}`); }); } lines.push(''); // Base URI if (spec.servers && spec.servers.length > 0) { lines.push(`baseUri: ${spec.servers[0].url}`); lines.push(''); } // Security Schemes if (spec.components && spec.components.securitySchemes) { lines.push('securitySchemes:'); for (const [name, scheme] of Object.entries(spec.components.securitySchemes)) { lines.push(` ${name}:`); if (scheme.type === 'apiKey') { lines.push(` type: Api Key`); lines.push(` describedBy:`); lines.push(` headers:`); lines.push(` Authorization:`); lines.push(` description: API key for authentication`); lines.push(` type: string`); } else if (scheme.type === 'http' && scheme.scheme === 'bearer') { lines.push(` type: OAuth 2.0`); lines.push(` settings:`); lines.push(` authorizationUri: ${scheme.bearerFormat || 'Bearer'}`); } } lines.push(''); } // Types (schemas) if (spec.components && spec.components.schemas) { lines.push('types:'); for (const [name, schema] of Object.entries(spec.components.schemas)) { lines.push(` ${name}:`); if (schema.type === 'object') { lines.push(` type: object`); if (schema.properties) { lines.push(` properties:`); for (const [propName, prop] of Object.entries(schema.properties)) { lines.push(` ${propName}:`); lines.push(` type: ${mapJsonTypeToRaml(prop.type || 'string')}`); if (prop.description) { lines.push(` description: ${prop.description}`); } } } } else { lines.push(` type: ${mapJsonTypeToRaml(schema.type || 'string')}`); } } lines.push(''); } // Paths if (spec.paths) { for (const [path, pathItem] of Object.entries(spec.paths)) { lines.push(`/${path.replace(/^\//, '')}:`); // Methods for (const [method, operation] of Object.entries(pathItem)) { if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) { lines.push(` ${method}:`); if (operation.summary) { lines.push(` displayName: ${operation.summary}`); } if (operation.description) { lines.push(` description: |`); operation.description.split('\n').forEach(line => { lines.push(` ${line}`); }); } // Query parameters if (operation.parameters) { const queryParams = operation.parameters.filter(p => p.in === 'query'); if (queryParams.length > 0) { lines.push(` queryParameters:`); queryParams.forEach(param => { lines.push(` ${param.name}:`); lines.push(` type: ${mapJsonTypeToRaml(param.schema?.type || 'string')}`); lines.push(` required: ${param.required || false}`); if (param.description) { lines.push(` description: ${param.description}`); } }); } } // Responses if (operation.responses) { lines.push(` responses:`); for (const [code, response] of Object.entries(operation.responses)) { lines.push(` ${code}:`); if (response.description) { lines.push(` description: ${response.description}`); } if (response.content && response.content['application/json']) { const schema = response.content['application/json'].schema; if (schema && schema.$ref) { const refName = schema.$ref.replace('#/components/schemas/', ''); lines.push(` body:`); lines.push(` application/json:`); lines.push(` type: ${refName}`); } } } } } } lines.push(''); } } return lines.join('\n'); } function mapJsonTypeToRaml(jsonType) { const typeMap = { 'string': 'string', 'integer': 'integer', 'number': 'number', 'boolean': 'boolean', 'array': 'array', 'object': 'object' }; return typeMap[jsonType] || 'string'; } async function main() { if (!fs.existsSync(INPUT_FILE)) { throw new Error(`Input file not found: ${INPUT_FILE}`); } console.log(`Reading OpenAPI 3.0 spec from ${INPUT_FILE}`); const spec = JSON.parse(fs.readFileSync(INPUT_FILE, 'utf-8')); console.log('Converting to RAML 1.0...'); const ramlContent = convertToRaml(spec); fs.writeFileSync(OUTPUT_FILE, ramlContent); console.log(`✓ RAML spec written to ${OUTPUT_FILE}`); console.log('RAML conversion complete'); } main() .then(() => { process.exit(0); }) .catch((error) => { console.error('Failed to convert to RAML:', error); process.exit(1); });