Created
June 5, 2025 03:16
-
-
Save jadsongmatos/a3b84b41f37667888016cc86334f7aa4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import copy | |
import json | |
from pathlib import Path | |
from typing import Any, Dict, Optional | |
def openapi_to_json_schema( | |
openapi_path: str, | |
output_path: str, | |
root_schema_name: Optional[str] = None | |
) -> None: | |
""" | |
Converte os componentes/schemas de um OpenAPI 3.x para um JSON Schema | |
2020-12 pronto para uso no react-jsonschema-form. | |
""" | |
# 1) Carrega OpenAPI | |
raw = Path(openapi_path).read_text(encoding="utf-8") | |
openapi: Dict[str, Any] = json.loads(raw) | |
schemas: Dict[str, Any] = ( | |
openapi.get("components", {}) | |
.get("schemas", {}) | |
) | |
if not schemas: | |
raise ValueError("O arquivo OpenAPI não contém components.schemas") | |
# 2) Funções auxiliares | |
def normalize_ref(node: Dict[str, Any]) -> None: | |
""" | |
Se houver "$ref": "#/components/schemas/X", converte → "#/$defs/X". | |
Deve ser chamada antes de qualquer outro processamento desse node. | |
""" | |
ref = node.get("$ref") | |
if isinstance(ref, str) and ref.startswith("#/components/schemas/"): | |
target = ref.split("/")[-1] # pega "X" em "#/components/schemas/X" | |
node["$ref"] = f"#/$defs/{target}" | |
def ensure_type(schema: Dict[str, Any]) -> None: | |
""" | |
Garante que exista a chave 'type' em schema. | |
Se não houver type, tenta inferir: | |
- se tiver "properties" ou "additionalProperties", assume "object" | |
- se tiver "items", assume "array" | |
- caso contrário, coloca "object" como fallback seguro | |
""" | |
if "type" in schema: | |
return | |
if "properties" in schema or "additionalProperties" in schema: | |
schema["type"] = "object" | |
elif "items" in schema: | |
schema["type"] = "array" | |
else: | |
schema["type"] = "object" | |
def transform(node: Any) -> Any: | |
""" | |
Aplica recursivamente as transformações a um sub-schema: | |
1) Se for lista, processa cada item | |
2) Se for primitivo, retorna direto | |
3) Se for dict: | |
a) normaliza "$ref" | |
b) converte "anyOf" → "oneOf" | |
c) percorre "properties", "patternProperties", "definitions", "$defs" | |
d) percorre "items" | |
e) percorre "oneOf" | |
f) garante "type" | |
""" | |
if isinstance(node, list): | |
return [transform(item) for item in node] | |
if not isinstance(node, dict): | |
return node | |
# 3) Normaliza ref antes de criar nodes aninhados | |
normalize_ref(node) | |
# 4) Converte anyOf → oneOf | |
if "anyOf" in node: | |
node["oneOf"] = node.pop("anyOf") | |
# 5) Percorre objetos aninhados | |
for key in ("properties", "patternProperties", "definitions", "$defs"): | |
if key in node and isinstance(node[key], dict): | |
node[key] = {k: transform(v) for k, v in node[key].items()} | |
# 6) Percorre items se existir | |
if "items" in node: | |
node["items"] = transform(node["items"]) | |
# 7) Percorre oneOf | |
if "oneOf" in node and isinstance(node["oneOf"], list): | |
node["oneOf"] = [transform(sub) for sub in node["oneOf"]] | |
# 8) Garante type | |
ensure_type(node) | |
return node | |
# 3) Copia e converte cada schema em components.schemas | |
converted: Dict[str, Any] = {} | |
for name, schema in schemas.items(): | |
# usa deepcopy para não modificar direto o original | |
converted[name] = transform(copy.deepcopy(schema)) | |
# 4) Escolhe o schema raiz | |
if root_schema_name is None: | |
# primeiramente, tenta tirar o "firstKey" de converted | |
root_schema_name = next(iter(converted)) | |
if root_schema_name not in converted: | |
raise KeyError(f"Schema '{root_schema_name}' não encontrado em components.schemas") | |
# 5) Extrai o schema raiz e coloca os demais em "$defs" | |
root_schema = converted.pop(root_schema_name) | |
root_schema.setdefault("$schema", "https://json-schema.org/draft/2020-12/schema") | |
root_schema.setdefault("$id", f"https://example.com/schemas/{root_schema_name}") | |
if converted: | |
# somente adiciona `$defs` se existir algum schema extra | |
root_schema["$defs"] = converted | |
# 6) Escreve em disco | |
Path(output_path).write_text( | |
json.dumps(root_schema, indent=2, ensure_ascii=False), | |
encoding="utf-8" | |
) | |
print(f"✅ Esquema convertido salvo em: {output_path}") | |
# gera JSON Schema | |
openapi_to_json_schema("openapi.json", "schema.json", root_schema_name=None) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment