#!/usr/bin/env python3 """ Systematically fix ALL Gin radix tree param name conflicts. Phase 2: Fix remaining bare :id routes that conflict with :resource_id sub-routes. Also fix handler files to match the new param names. """ import re import os # ===== Part 1: Fix router.go ===== router_path = '/home/yanghao05/Workspace/gochat/internal/router/router.go' with open(router_path) as f: content = f.read() # Replacement map for bare "/:id" → "/:resource_param" in route paths # Based on the group context (determined from line surroundings) replacements_router = { # Line ranges and their resource type → param name mapping # Format: (resource_pattern, new_param_name) } # Strategy: read line by line, check context (nearby Group variable comments) lines = content.split('\n') # Build a mapping from line content patterns to param names # We look for the variable that the route method is called on context_map = { 'drafts': 'draft_id', 'msgs': 'message_id', # messages group 'teams': 'team_id', 'platformApps': 'platform_app_id', # hmm, check Chatwoot 'agentBots': 'agent_bot_id', 'agentBotInboxes': 'agent_bot_inbox_id', # unique constraint 'botRules': 'rule_id', 'triggerConfigs': 'trigger_config_id', # hmm 'assistants': 'assistant_id', 'assistantDocs': 'document_id', 'assistantScenarios': 'scenario_id', 'copilotThreads': 'thread_id', # hmm, check Chatwoot 'automationRules': 'automation_id', 'macros': 'macro_id', 'participants': 'participant_id', # already has :conversation_id prefix 'whatsappCalls': 'call_id', } # Special handling for platform-level routes (not in accounts group) platform_level_map = { 'notifications': 'notification_id', 'push_subscriptions': 'push_subscription_id', } # Also fix the /accounts/:id routes (line ~1475) → /accounts/:account_id # These are outside the accounts group for i, line in enumerate(lines): stripped = line.strip() if stripped.startswith('//') or not stripped: continue # Check for bare "/:id" in route registration if not re.search(r'"/:id"', stripped): continue # Determine the group variable from the route method call rmatch = re.match(r'^(\w+)\.(GET|POST|PUT|PATCH|DELETE)\("([^"]+)"\s*,', stripped) if not rmatch: continue var_name = rmatch.group(1) path = rmatch.group(3) # Find the replacement param name new_param = context_map.get(var_name) # Check platform-level routes if var_name in ('g', 'v1'): for resource, param in platform_level_map.items(): if resource in path: new_param = param break # Special: account routes at platform level if var_name == 'g' and '/accounts/:id' in path: new_param = 'account_id' if new_param and new_param != 'id': new_path = path.replace('/:id', f'/:{new_param}') new_line = line.replace(f'"{path}"', f'"{new_path}"') lines[i] = new_line print(f"Router Line {i+1}: {var_name} {path} → {new_path} (:id → :{new_param})") new_content = '\n'.join(lines) with open(router_path, 'w') as f: f.write(new_content) # ===== Part 2: Fix handler files ===== # For each changed param name, find handler files that use parseUintParam(c, "id") # and change to parseUintParam(c, "new_param_name") # Also change the variable name from id to a more descriptive name handler_dir = '/home/yanghao05/Workspace/gochat/internal/handler/api/v1' handler_map = { # Map resource type to (handler_file, old_var, new_var, old_param, new_param) 'draft_id': ('draft_message_handler.go', 'id', 'draftID', '"id"', '"draft_id"'), 'message_id': ('message_handler.go', 'id', 'messageID', '"id"', '"message_id"'), 'team_id': ('team_handler.go', 'id', 'teamID', '"id"', '"team_id"'), 'agent_bot_id': ('agent_bot_handler.go', 'id', 'agentBotID', '"id"', '"agent_bot_id"'), 'automation_id': ('automation_rule_handler.go', 'id', 'automationID', '"id"', '"automation_id"'), 'macro_id': ('macro_handler.go', 'id', 'macroID', '"id"', '"macro_id"'), 'assistant_id': ('captain_assistant_handler.go', 'id', 'assistantID', '"id"', '"assistant_id"'), 'notification_id': ('notification_handler.go', 'id', 'notificationID', '"id"', '"notification_id"'), 'call_id': ('whatsapp_call_handler.go', 'id', 'callID', '"id"', '"call_id"'), 'participant_id': ('conversation_participant_handler.go', 'id', 'participantID', '"id"', '"participant_id"'), } print("\n=== Fixing handler files ===") for new_param, (handler_file, old_var, new_var, old_param_str, new_param_str) in handler_map.items(): filepath = os.path.join(handler_dir, handler_file) if not os.path.exists(filepath): print(f" SKIP: {handler_file} not found") continue with open(filepath) as f: hcontent = f.read() # Replace parseUintParam(c, "id") → parseUintParam(c, "new_param") # But be careful not to replace "account_id" or other specific params # Only replace bare "id" param extraction # Pattern: id, err := parseUintParam(c, "id") → newVar, err := parseUintParam(c, "new_param") old_pattern = f'{old_var}, err := parseUintParam(c, {old_param_str})' new_pattern = f'{new_var}, err := parseUintParam(c, {new_param_str})' if old_pattern in hcontent: hcontent = hcontent.replace(old_pattern, new_pattern) print(f" {handler_file}: Replaced '{old_pattern}' → '{new_pattern}'") # Also replace references to the old variable name (id → newVar) in service calls # Only replace bare `id` that was previously the conversation/resource ID # This is tricky — need to avoid replacing `id` in strings or other variable names # Replace patterns like: h.xxxSvc.Func(ctx, accountID, id) → h.xxxSvc.Func(ctx, accountID, newVar) # Pattern: , id) → , newVar) pattern_refs = f', {old_var})' replacement_refs = f', {new_var})' # But only for lines that DON'T contain accountID (to avoid double-replacing) # Actually we need to replace: accountID, id → accountID, newVar pattern_account_id = f'accountID, {old_var}' replacement_account_id = f'accountID, {new_var}' if pattern_account_id in hcontent: hcontent = hcontent.replace(pattern_account_id, replacement_account_id) print(f" {handler_file}: Replaced 'accountID, {old_var}' → 'accountID, {new_var}'") # Also replace bare id references that aren't part of other variable names # Like: id in function calls after the initial declaration was changed with open(filepath, 'w') as f: f.write(hcontent) print("\nDone! Now try: go build ./...")