Remove Godot OpenAPI generator script
It produced code that doesn't parse in Godot 4. Typed response classes will be generated in MusicPlayer instead (Sansan/MusicPlayer#40).
This commit is contained in:
1 parent
2c3e97190d
commit
bc7d406e41
1 file changed
-224
@@ -1,224 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import re
|
|
||||||
from pathlib import Path
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
def load_openapi_spec(path: str) -> dict:
|
|
||||||
"""Load OpenAPI spec from YAML or JSON file."""
|
|
||||||
try:
|
|
||||||
import yaml
|
|
||||||
with open(path, "r") as f:
|
|
||||||
return yaml.safe_load(f)
|
|
||||||
except ImportError:
|
|
||||||
# Fallback to JSON if PyYAML is not installed
|
|
||||||
with open(path, "r") as f:
|
|
||||||
return json.load(f)
|
|
||||||
|
|
||||||
def map_type(openapi_type: str) -> str:
|
|
||||||
"""Map OpenAPI types to GDScript types."""
|
|
||||||
type_mapping = {
|
|
||||||
"integer": "int",
|
|
||||||
"number": "float",
|
|
||||||
"boolean": "bool",
|
|
||||||
"array": "Array",
|
|
||||||
"object": "Dictionary",
|
|
||||||
}
|
|
||||||
return type_mapping.get(openapi_type, "String")
|
|
||||||
|
|
||||||
def default_value(openapi_type: str):
|
|
||||||
"""Return default values for GDScript types."""
|
|
||||||
default_mapping = {
|
|
||||||
"integer": "0",
|
|
||||||
"number": "0.0",
|
|
||||||
"boolean": "false",
|
|
||||||
"array": "[]",
|
|
||||||
"object": "{}",
|
|
||||||
}
|
|
||||||
return default_mapping.get(openapi_type, '""')
|
|
||||||
|
|
||||||
def sanitize_class_name(name: str) -> str:
|
|
||||||
"""Convert a name to a valid GDScript class name."""
|
|
||||||
# Replace invalid characters with underscores
|
|
||||||
name = re.sub(r"[^a-zA-Z0-9_]", "_", name)
|
|
||||||
# Capitalize first letter
|
|
||||||
return name[0].upper() + name[1:] if name else "Model"
|
|
||||||
|
|
||||||
def generate_model_class(class_name: str, schema: dict) -> str:
|
|
||||||
"""Generate a GDScript class for a model."""
|
|
||||||
lines = [
|
|
||||||
f"class_name {class_name}",
|
|
||||||
"extends RefCounted",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add properties
|
|
||||||
properties = schema.get("properties", {})
|
|
||||||
for prop_name, prop_schema in properties.items():
|
|
||||||
prop_type = map_type(prop_schema.get("type", "string"))
|
|
||||||
lines.append(f"var {prop_name}: {prop_type}")
|
|
||||||
|
|
||||||
# Add _init method
|
|
||||||
lines.extend([
|
|
||||||
"",
|
|
||||||
"func _init(data: Dictionary):",
|
|
||||||
])
|
|
||||||
for prop_name in properties:
|
|
||||||
prop_type = map_type(properties[prop_name].get("type", "string"))
|
|
||||||
default = default_value(properties[prop_name].get("type", "string"))
|
|
||||||
lines.append(f' {prop_name} = data.get("{prop_name}", {default})')
|
|
||||||
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
def generate_api_client(path: str, method: str, endpoint: dict) -> str:
|
|
||||||
"""Generate a GDScript API client for an endpoint."""
|
|
||||||
# Sanitize path for class name
|
|
||||||
class_name = sanitize_class_name(path.replace("/", "_").replace("{", "").replace("}", "")) + method.capitalize()
|
|
||||||
# Format URL (replace {param} with %s for Godot's string formatting)
|
|
||||||
url = path.replace("{", "%").replace("}", "s")
|
|
||||||
full_url = f'"{BASE_URL}{url}"'
|
|
||||||
|
|
||||||
lines = [
|
|
||||||
f"class_name {class_name}",
|
|
||||||
"extends RefCounted",
|
|
||||||
"",
|
|
||||||
"var http_request: HTTPRequest",
|
|
||||||
"",
|
|
||||||
"func _init(node: Node):",
|
|
||||||
" http_request = HTTPRequest.new()",
|
|
||||||
" node.add_child(http_request)",
|
|
||||||
' http_request.connect("request_completed", self, "_on_request_completed")',
|
|
||||||
"",
|
|
||||||
f"func call(params: Dictionary, callback: Callable):",
|
|
||||||
f" var url := {full_url}",
|
|
||||||
' var headers = ["User-Agent: MyGodotApp"]',
|
|
||||||
" var error := http_request.request(url, headers)",
|
|
||||||
" if error != OK:",
|
|
||||||
' push_error("HTTP request failed.")',
|
|
||||||
" return",
|
|
||||||
" http_request.set_meta(\"callback\", callback)",
|
|
||||||
"",
|
|
||||||
"func _on_request_completed(result: int, response_code: int, headers: PoolStringArray, body: PoolByteArray):",
|
|
||||||
" var callback := http_request.get_meta(\"callback\")",
|
|
||||||
" if callback:",
|
|
||||||
" var response_body = body.get_string_from_utf8()",
|
|
||||||
" var json = JSON.new()",
|
|
||||||
" if json.parse(response_body) == OK:",
|
|
||||||
" callback.call(json.get_data())",
|
|
||||||
" else:",
|
|
||||||
" callback.call(null)",
|
|
||||||
]
|
|
||||||
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
def generate_tag_client(tag: str, endpoints: list) -> str:
|
|
||||||
"""Generate a GDScript API client for all endpoints with a given tag."""
|
|
||||||
class_name = sanitize_class_name(tag) + "API"
|
|
||||||
|
|
||||||
lines = [
|
|
||||||
f"class_name {class_name}",
|
|
||||||
"extends RefCounted",
|
|
||||||
"",
|
|
||||||
"var http_request: HTTPRequest",
|
|
||||||
"var base_url: String",
|
|
||||||
"",
|
|
||||||
"func _init(node: Node, base_url_param: String):",
|
|
||||||
" http_request = HTTPRequest.new()",
|
|
||||||
" node.add_child(http_request)",
|
|
||||||
' http_request.connect("request_completed", self, "_on_request_completed")',
|
|
||||||
" base_url = base_url_param",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Generate a method for each endpoint
|
|
||||||
for path, method, endpoint in endpoints:
|
|
||||||
# Sanitize method name for GDScript
|
|
||||||
method_name = method.lower()
|
|
||||||
# Create a valid function name from the path
|
|
||||||
func_name = "call_" + path.replace("/", "_").replace("{", "").replace("}", "").replace("-", "_")
|
|
||||||
|
|
||||||
# Format URL (replace {param} with %s for Godot's string formatting)
|
|
||||||
url = path.replace("{", "%").replace("}", "s")
|
|
||||||
|
|
||||||
lines.extend([
|
|
||||||
f"func {func_name}(params: Dictionary = {{}}, callback: Callable):",
|
|
||||||
f' var url := base_url + "{url}"',
|
|
||||||
' var headers = ["User-Agent: MyGodotApp"]',
|
|
||||||
f" var error := http_request.request(url, headers, false, HTTPClient.METHOD_{method.upper()})",
|
|
||||||
" if error != OK:",
|
|
||||||
' push_error("HTTP request failed.")',
|
|
||||||
" return",
|
|
||||||
" http_request.set_meta(\"callback\", callback)",
|
|
||||||
"",
|
|
||||||
])
|
|
||||||
|
|
||||||
# Add the completion handler
|
|
||||||
lines.extend([
|
|
||||||
"func _on_request_completed(result: int, response_code: int, headers: PoolStringArray, body: PoolByteArray):",
|
|
||||||
" var callback := http_request.get_meta(\"callback\")",
|
|
||||||
" if callback:",
|
|
||||||
" var response_body = body.get_string_from_utf8()",
|
|
||||||
" var json = JSON.new()",
|
|
||||||
" if json.parse(response_body) == OK:",
|
|
||||||
" callback.call(json.get_data())",
|
|
||||||
" else:",
|
|
||||||
" callback.call(null)",
|
|
||||||
])
|
|
||||||
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_code(spec: dict, output_dir: str):
|
|
||||||
"""Generate all GDScript files from the OpenAPI spec."""
|
|
||||||
# Create output directory
|
|
||||||
Path(output_dir).mkdir(parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
# Generate models
|
|
||||||
schemas = spec.get("definitions", {}) # Swagger 2.0 uses "definitions"
|
|
||||||
if not schemas:
|
|
||||||
schemas = spec.get("components", {}).get("schemas", {})
|
|
||||||
|
|
||||||
for schema_name, schema in schemas.items():
|
|
||||||
class_name = sanitize_class_name(schema_name)
|
|
||||||
code = generate_model_class(class_name, schema)
|
|
||||||
output_path = Path(output_dir) / f"{class_name}.gd"
|
|
||||||
with open(output_path, "w") as f:
|
|
||||||
f.write(code)
|
|
||||||
print(f"Generated model: {output_path}")
|
|
||||||
|
|
||||||
# Group endpoints by tag
|
|
||||||
paths = spec.get("paths", {})
|
|
||||||
tag_endpoints = defaultdict(list)
|
|
||||||
|
|
||||||
for path, methods in paths.items():
|
|
||||||
for method, endpoint in methods.items():
|
|
||||||
tags = endpoint.get("tags", ["default"])
|
|
||||||
for tag in tags:
|
|
||||||
tag_endpoints[tag].append((path, method, endpoint))
|
|
||||||
|
|
||||||
# Generate one file per tag
|
|
||||||
for tag, endpoints in tag_endpoints.items():
|
|
||||||
code = generate_tag_client(tag, endpoints)
|
|
||||||
class_name = sanitize_class_name(tag) + "API"
|
|
||||||
output_path = Path(output_dir) / f"{class_name}.gd"
|
|
||||||
with open(output_path, "w") as f:
|
|
||||||
f.write(code)
|
|
||||||
print(f"Generated API client for tag '{tag}': {output_path}")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
parser = argparse.ArgumentParser(description="Generate Godot API clients from OpenAPI spec")
|
|
||||||
parser.add_argument("input", help="Path to the OpenAPI JSON/YAML file")
|
|
||||||
parser.add_argument("-o", "--output", default="godot_generated", help="Output directory for GDScript files")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
spec = load_openapi_spec(args.input)
|
|
||||||
generate_code(spec, args.output)
|
|
||||||
print("Done!")
|
|
||||||
print("Note: When initializing the API classes, pass the base URL as a parameter:")
|
|
||||||
print(" var music_api = MusicAPI.new()")
|
|
||||||
print(" music_api._init(get_node('/root'), 'http://localhost:8080')")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Reference in new issue
Block a user