37bed1cd4e
Docs Check / Markdown lint (push) Successful in 1m6s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 1m20s
Build and Push Docker Image / build (push) Successful in 1m35s
CI / Swagger Validation & Coverage (push) Failing after 2m0s
CI / Security audit (push) Successful in 2m6s
Docs Check / Mermaid diagram parse check (push) Successful in 2m20s
CI / Tests & coverage (push) Failing after 2m30s
- Add RAML generation scripts (generate-openapi, downgrade-openapi, simple-raml-converter, package-raml) - Add /api/swagger.json endpoint to server/app.js - Add minimal .spectral.yml ruleset for OpenAPI linting - Add npm scripts for OpenAPI/RAML generation and packaging - Extend CI swagger job with RAML generation steps - Upload raml-package artifact with 14-day retention - Update CHANGELOG.md for v1.7.1
184 lines
5.8 KiB
JavaScript
184 lines
5.8 KiB
JavaScript
// 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);
|
|
});
|