mirror of
https://github.com/matrix-org/matrix-spec
synced 2026-03-26 13:04:10 +01:00
Revert "Create Swagger 2.0 to OpenAPI 3.1 conversion scripts"
This reverts commit 667c39f259.
This commit is contained in:
parent
902e38189f
commit
e2efbfbe5c
|
|
@ -1,83 +0,0 @@
|
||||||
"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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
"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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
"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;
|
|
||||||
}
|
|
||||||
1292
scripts/openapi-convert/package-lock.json
generated
1292
scripts/openapi-convert/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"name": "openapi-convert",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"nopt": "^3.0.2",
|
|
||||||
"replace": "^1.2.2",
|
|
||||||
"swagger2openapi": "^7.0.8",
|
|
||||||
"yaml": "^2.1.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
"use strict";
|
|
||||||
import fs from 'node:fs/promises';
|
|
||||||
import path_utils from 'node:path';
|
|
||||||
import yaml from 'yaml';
|
|
||||||
|
|
||||||
const DEFAULT_INDENT = 2;
|
|
||||||
|
|
||||||
// Find the first child field with the given indentation in the given content.
|
|
||||||
//
|
|
||||||
// Returns the position at the end of the field's line.
|
|
||||||
function findYamlChildField(content, min_indent, field_name) {
|
|
||||||
const min_indent_string = " ".repeat(min_indent);
|
|
||||||
const field_line = min_indent_string + field_name + ":\n";
|
|
||||||
let line_start_idx = 0;
|
|
||||||
|
|
||||||
while (line_start_idx < content.length) {
|
|
||||||
const content_end = content.slice(line_start_idx);
|
|
||||||
const line_length = content_end.indexOf("\n") + 1;
|
|
||||||
const line_end_idx = line_start_idx + line_length;
|
|
||||||
const line = content_end.slice(0, line_length);
|
|
||||||
|
|
||||||
if (line == field_line) {
|
|
||||||
// This is the child we are looking for.
|
|
||||||
return line_end_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!line.startsWith(min_indent_string)) {
|
|
||||||
// We changed the parent so we can't find the child anymore.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
line_start_idx = line_end_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We didn't find the child.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the end of the children with the given indentation in the YAML content.
|
|
||||||
//
|
|
||||||
// Returns the position at the end of the last child.
|
|
||||||
function findYamlChildrenEnd(content, min_indent) {
|
|
||||||
const min_indent_string = " ".repeat(min_indent);
|
|
||||||
let line_start_idx = 0;
|
|
||||||
|
|
||||||
while (line_start_idx < content.length) {
|
|
||||||
const content_end = content.slice(line_start_idx);
|
|
||||||
|
|
||||||
if (content_end.startsWith(min_indent_string)) {
|
|
||||||
const line_length = content_end.indexOf("\n") + 1;
|
|
||||||
line_start_idx += line_length;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return line_start_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert and replace the YAML in the given content between start and end with JSON.
|
|
||||||
function replaceYamlWithJson(content, start, end, extra_indent) {
|
|
||||||
console.log("Processing example", start, end);
|
|
||||||
const example_yaml = content.slice(start, end);
|
|
||||||
console.log("```" + example_yaml + "```");
|
|
||||||
const example_obj = yaml.parse(example_yaml);
|
|
||||||
const example_json = JSON.stringify(example_obj, null, DEFAULT_INDENT) + "\n";
|
|
||||||
console.log("```" + example_json + "```");
|
|
||||||
|
|
||||||
// Fix the indentation.
|
|
||||||
let json_lines = example_json.split("\n");
|
|
||||||
// The first and last line don't need the extra indent.
|
|
||||||
for (let i = 1; i < json_lines.length - 1; i++) {
|
|
||||||
json_lines[i] = " ".repeat(extra_indent) + json_lines[i];
|
|
||||||
}
|
|
||||||
const indented_example_json = json_lines.join("\n");
|
|
||||||
|
|
||||||
// Put the opening bracket on the same line as the parent field.
|
|
||||||
const replace_start = start - 1;
|
|
||||||
content = content.slice(0, replace_start) + ' ' + indented_example_json + content.slice(end);
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert the examples under the given fields in the YAML content to JSON.
|
|
||||||
function convertExamplesToJson(content, parent_field, example_field) {
|
|
||||||
const parent_field_regex_string = "( +)" + parent_field + ":\n";
|
|
||||||
const parent_field_regex = RegExp(parent_field_regex_string, 'g');
|
|
||||||
let match;
|
|
||||||
let examples = [];
|
|
||||||
|
|
||||||
while ((match = parent_field_regex.exec(content)) !== null) {
|
|
||||||
console.log("Found parent field", parent_field, match.index);
|
|
||||||
const indent_capture = match[1];
|
|
||||||
const example_field_line_indent = indent_capture.length + DEFAULT_INDENT;
|
|
||||||
const parent_field_line_end = parent_field_regex.lastIndex;
|
|
||||||
let content_end = content.slice(parent_field_line_end);
|
|
||||||
|
|
||||||
const example_field_line_end = findYamlChildField(content_end, example_field_line_indent, example_field);
|
|
||||||
|
|
||||||
if (example_field_line_end == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const example_start = parent_field_line_end + example_field_line_end;
|
|
||||||
content_end = content.slice(example_start);
|
|
||||||
console.log("Found example at", example_start);
|
|
||||||
const example_line_min_indent = example_field_line_indent + DEFAULT_INDENT;
|
|
||||||
const example_length = findYamlChildrenEnd(content_end, example_line_min_indent);
|
|
||||||
console.log("Example length", example_length);
|
|
||||||
|
|
||||||
if (example_length > 0) {
|
|
||||||
examples.push({
|
|
||||||
start: example_start,
|
|
||||||
end: example_start + example_length,
|
|
||||||
extra_indent: example_field_line_indent,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const example of examples.reverse()) {
|
|
||||||
content = replaceYamlWithJson(content, example.start, example.end, example.extra_indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function applyStringFixesFile(path) {
|
|
||||||
const relative_separator = path.lastIndexOf('/data/api');
|
|
||||||
const short_path = path.slice(relative_separator + 1);
|
|
||||||
console.log("%s", short_path);
|
|
||||||
|
|
||||||
let content = await fs.readFile(path, "utf-8");
|
|
||||||
|
|
||||||
// Fix occurrences of `*xx` status codes to `*XX`.
|
|
||||||
content = content.replaceAll("3xx:", "\"3XX\":");
|
|
||||||
content = content.replaceAll("4xx:", "\"4XX\":");
|
|
||||||
|
|
||||||
// Fix occurrences of `_ref` to `$ref`.
|
|
||||||
content = content.replaceAll("_ref:", "$ref:");
|
|
||||||
|
|
||||||
// Fix occurrences of `x-example` to `example`.
|
|
||||||
content = content.replaceAll("x-example:", "example:");
|
|
||||||
|
|
||||||
// Convert examples back to JSON.
|
|
||||||
// Response examples.
|
|
||||||
content = convertExamplesToJson(content, "response", "value");
|
|
||||||
// Schema examples.
|
|
||||||
content = convertExamplesToJson(content, "schema", "example");
|
|
||||||
|
|
||||||
await fs.writeFile(path, content);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixes to apply to the string content of the files.
|
|
||||||
export async function applyStringFixes(path) {
|
|
||||||
const stat = await fs.lstat(path);
|
|
||||||
if (stat.isDirectory()) {
|
|
||||||
const files = await fs.readdir(path);
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
await applyStringFixes(path_utils.join(path, file))
|
|
||||||
}
|
|
||||||
} else if (stat.isFile()) {
|
|
||||||
await applyStringFixesFile(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue