From bc7d406e4105539e0033caea1211e309fefa639d Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 6 Oct 2026 16:12:05 +0200 Subject: [PATCH] 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). --- cmd/docs/generate_godot_openapi.py | 224 ----------------------------- 1 file changed, 224 deletions(-) delete mode 100644 cmd/docs/generate_godot_openapi.py diff --git a/cmd/docs/generate_godot_openapi.py b/cmd/docs/generate_godot_openapi.py deleted file mode 100644 index 795d0d8..0000000 --- a/cmd/docs/generate_godot_openapi.py +++ /dev/null @@ -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()