Skip to content

Commit

Permalink
Create Swagger 2.0 to OpenAPI 3.1 conversion scripts
Browse files Browse the repository at this point in the history
Signed-off-by: Kévin Commaille <zecakeh@tedomum.fr>
  • Loading branch information
zecakeh committed May 9, 2023
1 parent dc5d95c commit 667c39f
Show file tree
Hide file tree
Showing 6 changed files with 1,646 additions and 0 deletions.
83 changes: 83 additions & 0 deletions scripts/openapi-convert/convert-definitions.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use strict";
import fs from 'node:fs/promises';
import converter from 'swagger2openapi';
import path_utils from 'node:path';
import yaml from 'yaml';

import { applyObjectFixes } from './object-fixes.mjs';

async function getStartComment(path) {
const file = await fs.open(path);

let start_comment = "";
for await (const line of file.readLines()) {
if (line.startsWith('#')) {
start_comment += line + '\n';
} else {
break;
}
};

await file.close();

return start_comment
}

async function convertDefinitionsFile(path) {
let relative_separator = path.lastIndexOf('/data/api');
let short_path = path.slice(relative_separator + 1);
console.log("%s", short_path);

// Save the comments at the start of the file to not lose them.
let start_comment = await getStartComment(path);

// Convert.
const options = await converter.convertFile(path, {
// Patch fixable errors.
patch: true,
// Keep $ref siblings.
refSiblings: 'preserve',
// Don't deduplicate requestBodies.
resolveInternal: true,
// Write OpenAPI version 3.1.0, even if it's not completely true, it'll
// be fixed later.
targetVersion: '3.1.0',
});

// Apply fixes on object.
const obj = applyObjectFixes(options.openapi);

// Serialize.
const doc = new yaml.Document(obj);
const content = yaml.stringify(doc, {
// Use "literal" blocks, like the input.
blockQuote: "literal"
});

// Save to file.
await fs.writeFile(path, start_comment + content);
}

export async function convertDefinitionsDir(path) {
console.log("Converting files in %s", path);

const files = await fs.readdir(path);

for (const file of files) {
if (file.endsWith(".yaml")) {
await convertDefinitionsFile(path_utils.join(path, file));
}
}
}

// Convert Swagger 2.0 definitions to OpenAPI 3.0.0.
export async function convertDefinitions(path) {
// We don't want to try to convert schemas in subdirectories, so we need to
// call this separately for every directory inside `data/api`.
let api_dir = path_utils.join(path, "api");
const files = await fs.readdir(api_dir);

for (const file of files) {
await convertDefinitionsDir(path_utils.join(api_dir, file));
}
}
40 changes: 40 additions & 0 deletions scripts/openapi-convert/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"use strict";
import nopt from 'nopt';

import { convertDefinitions } from './convert-definitions.mjs';
import { applyStringFixes } from './string-fixes.mjs';

const opts = nopt({
"help": Boolean,
"data": String
}, {
"h": "--help",
"d": "--data"
});

console.log("params: {}", opts);
if (opts.help) {
console.log(
"Convert the definitions from Swagger 2.0 to OpenAPI 3.0\n" +
"Usage:\n" +
" node index.mjs -d <data_folder>"
);
process.exit(0);
}
if (!opts.data) {
console.error("No [d]ata dir specified.");
process.exit(1);
}

console.log("Converting Swagger 2.0 definitions to OpenAPI 3.0 in %s...", opts.data);
try {
await convertDefinitions(opts.data);

console.log("Applying string fixes...");
await applyStringFixes(opts.data);

console.log(" ✅ Success");
} catch (err) {
console.error(' ❌ {}', err);
process.exit(1);
}
49 changes: 49 additions & 0 deletions scripts/openapi-convert/object-fixes.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"use strict";
import { URL } from 'node:url';

// Refactor `servers` field to be able to access `basePath` easily.
function refactorServers(obj) {
if (!obj.servers) {
return;
}

let server = {
url: "",
variables: {
protocol: {},
hostname: {
default: ""
},
basePath: {
default: ""
}
}
}

const url = new URL(obj.servers[0].url);

if (obj.servers.length > 1) {
// In our case several URLs always mean both http and https for the same
// host.
obj.servers.pop()
}

server.url = "{protocol}://{hostname}{basePath}"
server.variables.protocol = {
enum: ["http", "https"],
default: "https",
}
server.variables.hostname.default = url.host
server.variables.basePath.default = url.pathname

obj.servers[0] = server;

return obj;
}

// Fixes to apply to a converted schema object.
export function applyObjectFixes(obj) {
obj = refactorServers(obj);

return obj;
}
Loading

0 comments on commit 667c39f

Please sign in to comment.