Files
remnawave-api-go/scripts/generate_clientext_final.py
T

235 lines
7.2 KiB
Python
Executable File

#!/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)