#!/usr/bin/env python3 """ Final client_ext.go generator that actually works. Reads api-2-2-2-consolidated.json and oas_client_gen.go """ import json import re print("=" * 70) print("CLIENT_EXT.GO GENERATOR") print("=" * 70) # Step 1: Parse oas_client_gen.go for method signatures print("\n[1/4] Parsing oas_client_gen.go...") with open('api/oas_client_gen.go', 'r') as f: content = f.read() # Extract method signatures more carefully methods = {} # Match: func (c *Client) MethodName(ctx context.Context, ...) (...) { pattern = r'func \(c \*Client\) (\w+)\((ctx context\.Context(?:,\s*[^)]+)?)\)\s*\(([^)]+)\)' for match in re.finditer(pattern, content, re.MULTILINE): method_name = match.group(1) if method_name in ['requestURL', 'sendApiTokensControllerCreate']: # Skip internal continue if method_name.startswith('send'): continue full_params = match.group(2) # "ctx context.Context, request *Type, params ParamsType" returns = match.group(3) # "TypeRes, error" # Parse params (skip ctx) params_list = [] if ', ' in full_params: params_str = full_params.split(', ', 1)[1] # Remove "ctx context.Context" # Split remaining params carefully for param in re.findall(r'(\w+)\s+([\*\w\.]+)', params_str): params_list.append((param[0], param[1])) # Parse returns returns_list = [r.strip() for r in returns.split(',')] methods[method_name] = { 'params': params_list, 'returns': returns_list } print(f" ✓ Found {len(methods)} client methods") # Step 2: Parse api-2-2-2-consolidated.json for operations print("\n[2/4] Parsing api-2-2-2-consolidated.json...") with open('api-2-2-2-consolidated.json', 'r') as f: spec = json.load(f) operations_by_controller = {} for path, path_item in spec.get('paths', {}).items(): for http_method, op_spec in path_item.items(): if http_method not in ['get', 'post', 'put', 'patch', 'delete']: continue op_id = op_spec.get('operationId') if not op_id or '_' not in op_id: continue # Parse: "ApiTokensController_create" -> controller="ApiTokensController", method="create" parts = op_id.split('_', 1) controller_full = parts[0] # e.g., "ApiTokensController" method_snake = parts[1] # e.g., "create" # Controller name without "Controller" suffix controller = controller_full.replace('Controller', '') # Convert method to PascalCase: findAll -> FindAll, get_status -> GetStatus # Just capitalize first letter of each word, preserve rest def to_pascal(s): if not s: return s # Capitalize first letter, keep rest as-is return s[0].upper() + s[1:] # Split by underscore and capitalize each part parts = method_snake.split('_') method_pascal = ''.join(to_pascal(p) for p in parts) # The actual Go method name in oas_client_gen.go go_method = controller_full + method_pascal # e.g., "ApiTokensControllerCreate" if controller not in operations_by_controller: operations_by_controller[controller] = [] operations_by_controller[controller].append({ 'operationId': op_id, 'goMethod': go_method, 'displayMethod': method_pascal }) total_ops = sum(len(ops) for ops in operations_by_controller.values()) print(f" ✓ Found {total_ops} operations in {len(operations_by_controller)} controllers") # Step 3: Generate code print("\n[3/4] Generating code...") def to_camel(s): """PascalCase -> camelCase""" return s[0].lower() + s[1:] if s else s code = '''// Code generated by generate_clientext_final.py. DO NOT EDIT manually. // Generated from api-2-2-2-consolidated.json with renamed schemas. package api import "context" // ClientExt wraps the base Client with organized sub-client access. type ClientExt struct { \t*Client ''' # Add fields for each controller for controller in sorted(operations_by_controller.keys()): field_name = to_camel(controller) code += f'\t{field_name} *{controller}Client\n' code += '''} // NewClientExt creates a new ClientExt wrapper. func NewClientExt(client *Client) *ClientExt { \treturn &ClientExt{ \t\tClient: client, ''' # Initialize fields for controller in sorted(operations_by_controller.keys()): field_name = to_camel(controller) code += f'\t\t{field_name}: New{controller}Client(client),\n' code += '''\t} } ''' # Accessor methods for controller in sorted(operations_by_controller.keys()): field_name = to_camel(controller) code += f'''// {controller} returns the {controller}Client. func (ce *ClientExt) {controller}() *{controller}Client {{ \treturn ce.{field_name} }} ''' # Sub-client types and methods for controller in sorted(operations_by_controller.keys()): code += f'''// {controller}Client provides {controller} operations. type {controller}Client struct {{ \tclient *Client }} // New{controller}Client creates a new {controller}Client. func New{controller}Client(client *Client) *{controller}Client {{ \treturn &{controller}Client{{client: client}} }} ''' # Generate methods for this controller for op in sorted(operations_by_controller[controller], key=lambda x: x['goMethod']): go_method = op['goMethod'] display_method = op['displayMethod'] op_id = op['operationId'] if go_method not in methods: print(f" ⚠ Warning: {go_method} not found in oas_client_gen.go") continue method_info = methods[go_method] params = method_info['params'] returns = method_info['returns'] # Build parameter list if params: params_sig = ', '.join([f'{p[0]} {p[1]}' for p in params]) params_call = ', '.join([p[0] for p in params]) else: params_sig = '' params_call = '' # Build return type if returns: ret_type = ', '.join(returns) if len(returns) > 1: ret_type = f'({ret_type})' else: ret_type = '' # Generate method code += f'''// {display_method} calls {op_id}. func (sc *{controller}Client) {display_method}(ctx context.Context''' if params_sig: code += f', {params_sig}' code += ')' if ret_type: code += f' {ret_type}' code += ' {\n' if returns: code += f'\treturn sc.client.{go_method}(ctx' else: code += f'\tsc.client.{go_method}(ctx' if params_call: code += f', {params_call}' code += ')\n}\n\n' # Step 4: Write to file print("\n[4/4] Writing api/client_ext.go...") with open('api/client_ext.go', 'w') as f: f.write(code) print("\n" + "=" * 70) print(f"✅ SUCCESS!") print("=" * 70) print(f" Controllers: {len(operations_by_controller)}") print(f" Operations: {total_ops}") print(f" File: api/client_ext.go") print(f" Uses: api-2-2-2-consolidated.json (renamed schemas)") print("=" * 70)