235 lines
7.2 KiB
Python
Executable File
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)
|