Files
gochat/scripts/fix_all_route_params.py
T
2026-06-04 15:44:48 +08:00

221 lines
9.6 KiB
Python

#!/usr/bin/env python3
"""
Fix all Gin radix tree param name conflicts in router.go.
Strategy: Replace generic `:id` with specific resource param names everywhere.
Rules (based on Chatwoot convention and Gin tree requirements):
- accounts/:id → :account_id (but most already use :account_id)
- conversations/:id → :conversation_id (already done)
- messages/:id → :message_id
- agent_bots/:id → :agent_bot_id
- assistants/:id → :assistant_id
- inboxes/:id → :inbox_id (but some already use :inbox_id)
- teams/:id → :team_id
- contacts/:id → :contact_id
- campaigns/:id → :campaign_id
- labels/:id → :label_id
- automation_rules/:id → :automation_id
- portals/:id → :portal_id
- categories/:id → :category_id
- articles/:id → :article_id
- canned_responses/:id → :canned_id
- macros/:id → :macro_id
- custom_attribute_definitions/:id → :attribute_id
- csat_survey_responses/:id → :csat_id
- notes/:id → :note_id
- webhook_subscriptions/:id → :webhook_id
- team_members/:id → :member_id (or :user_id)
- dashboard_apps/:id → :dashboard_app_id
- notification_subscriptions/:identifier → keep (no conflict)
- push_subscriptions/:id → :push_subscription_id
- notifications/:id → :notification_id
- draft_messages/:id → :draft_id
- contacts/:id/custom_attributes → :contact_id
- companies/:id → :company_id
- sla_policies/:id → :policy_id
- custom_roles/:id → :custom_role_id
- folders/:id → :folder_id
- inbox_members/:id → :member_id
- hooks/:id → :hook_id
"""
import re
router_path = '/home/yanghao05/Workspace/gochat/internal/router/router.go'
handler_dir = '/home/yanghao05/Workspace/gochat/internal/handler/api/v1'
# Read router.go
with open(router_path) as f:
router_content = f.read()
router_lines = router_content.split('\n')
# Context-aware replacement map
# For each resource type, map :id → specific param name
# We need to know which Group variable the route is under
# First pass: extract group variable → resource type mapping
# e.g. conversations := conversations.Group(...) → conversations var handles conversation routes
# msgs := conversations.Group("/:conversation_id/messages") → msgs var handles message routes
# Build a mapping of: (group_var_name → (parent_group_var, path_segment, specific_param_name))
# Then when we see varName.GET("/:id", ...), we know what :id should be renamed to
# Simplified approach: look at the route context to determine the resource type
# If route is under "conversations" group → :id means :conversation_id
# If route is under "msgs" or "messages" group → :id means :message_id
# etc.
# Let's trace the group hierarchy from the router.go
group_map = {} # var_name → (parent_var, sub_path, implied_resource_type)
# Parse group definitions
for i, line in enumerate(router_lines):
stripped = line.strip()
# Match: varName := parent.Group("/path/:param/...")
gmatch = re.match(r'(\w+)\s*:?=\s*(\w+)\.Group\("([^"]+)"\)', stripped)
if gmatch:
var_name = gmatch.group(1)
parent = gmatch.group(2)
path = gmatch.group(3)
group_map[var_name] = (parent, path)
# Now for each line with a bare :id in a route, determine what it should be renamed to
# based on context (which group variable is used, what resource type it represents)
replacements_made = []
for i, line in enumerate(router_lines):
stripped = line.strip()
if stripped.startswith('//') or not stripped:
continue
# Find route registrations with bare :id
# Pattern: varName.GET("/:id", ...) or varName.GET("/:id/subpath", ...)
# We need to determine which resource type :id refers to
# Check if line has a bare :id (not part of a longer name)
if not re.search(r'"/:id(?:"|/)', stripped):
continue
# Determine the resource type from the group variable name and context
for method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'Any']:
pattern = rf'^(\w+)\.{method}\("([^"]+)"\s*,'
rmatch = re.match(pattern, stripped)
if rmatch:
var_name = rmatch.group(1)
path = rmatch.group(2)
# Trace group hierarchy to determine resource type
# Walk up from var_name through parent groups
resource_param = None
# Special cases based on variable name patterns
var_lower = var_name.lower()
if var_name == 'conversations':
resource_param = 'conversation_id'
elif var_name in ('msgs', 'messages') or 'message' in var_lower:
resource_param = 'message_id'
elif var_name == 'agents' or var_name == 'agentBotGroup':
resource_param = 'agent_id'
elif var_name == 'agentBots' or var_name == 'agentBotsGroup':
resource_param = 'agent_bot_id'
elif var_name == 'teams':
resource_param = 'team_id'
elif var_name == 'inboxes' or var_name == 'inboxGroup':
resource_param = 'inbox_id'
elif var_name == 'contacts' or var_name == 'contactGroup':
resource_param = 'contact_id'
elif var_name == 'campaigns' or var_name == 'campaignGroup':
resource_param = 'campaign_id'
elif var_name == 'labels':
resource_param = 'label_id'
elif var_name == 'automationRules' or var_name == 'automationGroup':
resource_param = 'automation_id'
elif var_name == 'portals' or var_name == 'portalGroup':
resource_param = 'portal_id'
elif var_name == 'categories':
resource_param = 'category_id'
elif var_name == 'articles':
resource_param = 'article_id'
elif var_name == 'cannedResponses' or var_name == 'cannedGroup':
resource_param = 'canned_id'
elif var_name == 'macros':
resource_param = 'macro_id'
elif var_name == 'customAttrDefs' or var_name == 'customAttrGroup':
resource_param = 'attribute_id'
elif var_name == 'notes' or var_name == 'noteGroup':
resource_param = 'note_id'
elif var_name == 'teamMembers':
resource_param = 'member_id'
elif var_name == 'dashboardApps':
resource_param = 'dashboard_app_id'
elif var_name == 'drafts':
resource_param = 'draft_id'
elif var_name == 'participants' or var_name == 'participantGroup':
resource_param = 'participant_id'
elif var_name == 'companies':
resource_param = 'company_id'
elif var_name == 'slaPolicies' or var_name == 'slaGroup':
resource_param = 'policy_id'
elif var_name == 'customRoles':
resource_param = 'custom_role_id'
elif var_name == 'folders':
resource_param = 'folder_id'
elif var_name == 'hooks' or var_name == 'hookGroup':
resource_param = 'hook_id'
elif var_name == 'assistants' or var_name == 'assistantGroup':
resource_param = 'assistant_id'
elif var_name == 'documents':
resource_param = 'document_id'
elif var_name == 'tools' or var_name == 'toolGroup':
resource_param = 'tool_id'
elif var_name == 'inboxMembers':
resource_param = 'member_id'
elif var_name == 'whatsappCalls':
resource_param = 'call_id'
elif var_name == 'botRules' or var_name == 'botRuleGroup':
resource_param = 'rule_id'
elif var_name == 'preferences':
resource_param = 'preference_id'
elif var_name == 'scenarios':
resource_param = 'scenario_id'
elif var_name == 'insights':
resource_param = 'conversation_id' # insights are conversation-scoped
# Platform-level routes (not under accounts group)
elif var_name == 'g' or var_name == 'v1':
# Check path context
if '/notifications' in path or '/notification' in path:
resource_param = 'notification_id'
elif '/push_subscriptions' in path:
resource_param = 'push_subscription_id'
else:
# Check nearby lines for context
resource_param = None
# accountScoped routes
elif var_name == 'accountScoped' or var_name == 'accounts':
# Path might be like "/:id" which means account_id
if path == '/:id' or path.startswith('/:id'):
resource_param = 'account_id'
if resource_param and resource_param != 'id':
# Replace :id with :resource_param in the path
new_path = path.replace('/:id"', f'/:{resource_param}"')
new_path = new_path.replace('/:id/', f'/:{resource_param}/')
if new_path != path:
# Replace in the original line
new_line = line.replace(f'"{path}"', f'"{new_path}"')
router_lines[i] = new_line
replacements_made.append((i+1, var_name, path, new_path, resource_param))
break
# Write back
new_content = '\n'.join(router_lines)
with open(router_path, 'w') as f:
f.write(new_content)
print(f"Made {len(replacements_made)} replacements in router.go:")
for linenum, var, old, new, param in replacements_made:
print(f" Line {linenum}: {var} {old}{new} (:id → :{param})")