Spaces:
Sleeping
Sleeping
File size: 7,280 Bytes
6f69304 9206cfd 6f69304 099ebb2 6f69304 4073163 6f69304 82b7c1e 6f69304 4073163 099ebb2 9206cfd 4073163 9206cfd 4073163 6f69304 4073163 6f69304 4073163 89dff06 099ebb2 4073163 fea3cfe 4073163 fea3cfe 4073163 fea3cfe 4073163 fea3cfe 4073163 fea3cfe 6b4df33 fea3cfe 6b4df33 fea3cfe 3ceded0 4073163 3ceded0 4073163 3ceded0 25b1d7a 3ceded0 4073163 099ebb2 4073163 6f69304 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | from fastapi import APIRouter, Header, Request, HTTPException
import logging
from services.supabase_service import supabase_service
from integrations.jira_service import JiraIntegrationService
from typing import Dict, Any
from datetime import datetime
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/webhooks/jira", tags=["Webhooks"])
@router.post("")
async def receive_jira_webhook(
request: Request,
x_atlassian_webhook_identifier: str = Header(None)
):
"""
Receive webhook events from Jira and sync data using the user's stored credentials
"""
try:
payload = await request.json()
logger.info(f"Received Jira webhook: {x_atlassian_webhook_identifier}")
# 1. Extract Jira Account ID from payload
account_id = None
if "user" in payload and "accountId" in payload["user"]:
account_id = payload["user"]["accountId"]
elif "issue" in payload and payload["issue"].get("fields", {}).get("assignee"):
account_id = payload["issue"]["fields"]["assignee"].get("accountId")
elif "issue" in payload and payload["issue"].get("fields", {}).get("reporter"):
account_id = payload["issue"]["fields"]["reporter"].get("accountId")
if not account_id:
logger.warning("Could not identify Jira Account ID from webhook payload")
logger.warning(f"Payload snippet: {str(payload)[:500]}...")
return {"status": "ignored", "reason": "no_account_id_found"}
# 2. Find user credentials in Supabase using Account ID
user_creds = supabase_service.get_user_credentials_by_account_id(account_id)
if not user_creds:
logger.warning(f"No Supabase user found with Jira Account ID: {account_id}")
# This is expected for users who haven't logged in yet with the new auth flow
return {"status": "ignored", "reason": "user_not_linked"}
firebase_id = user_creds.get("firebase_id")
jira_token = user_creds.get("jira_api_token")
jira_url = user_creds.get("jira_server_url")
jira_email = user_creds.get("jira_email")
if not jira_token or not jira_url:
logger.warning(f"User {firebase_id} found but missing Jira credentials")
return {"status": "ignored", "reason": "credentials_missing"}
# 3. Initialize Jira Service with USER'S credentials
try:
jira_service = JiraIntegrationService(
jira_email=jira_email,
jira_api_token=jira_token,
jira_server_url=jira_url
)
logger.info(f"Initialized Jira service for user {jira_email} ({account_id})")
# 4. Fetch all data
full_data = {
"webhook_event": payload,
"synced_at": datetime.utcnow().isoformat(),
"assigned_issues": [],
"projects_data": [], # Renamed to store rich data
"boards": []
}
# A. Fetch Assigned Issues (Just for quick reference)
assigned_issues = jira_service.get_issues_assigned_to_user(account_id)
full_data["assigned_issues"] = [issue.model_dump(mode='json') for issue in assigned_issues]
# B. Fetch All Projects & Their Contents (Spaces)
projects = jira_service.get_projects()
logger.info(f"Found {len(projects)} projects for user. Fetching details...")
for project in projects:
# 1. Fetch All Issues in Project (Limit 50 to avoid timeout for now, can increase)
# We need a method to get issues by project key
try:
project_issues = jira_service.get_issues_by_project(project.project_key, max_results=50)
full_data["projects_data"].append({
"project_info": project.model_dump(mode='json'),
"issues": [issue.model_dump(mode='json') for issue in project_issues]
})
except Exception as e:
logger.error(f"Failed to fetch issues for project {project.project_key}: {str(e)}")
# Add project without issues in worst case
full_data["projects_data"].append({
"project_info": project.model_dump(mode='json'),
"issues": []
})
# C. Fetch All Boards & Sprints
boards = jira_service.get_boards()
enhanced_boards = []
logger.info(f"Found {len(boards)} boards. Fetching sprints...")
for board in boards:
board_data = board.copy()
board_id = board.get('id')
# Check Board Type
board_type = board.get('type')
# A. Scrum Boards: Fetch Sprints
if board_type == 'scrum':
try:
sprints = jira_service.get_sprints(board_id)
board_data['sprints'] = [sprint.model_dump(mode='json') for sprint in sprints]
except Exception as e:
logger.warning(f"Could not fetch sprints for Scrum board {board_id}: {str(e)}")
board_data['sprints'] = []
# B. Kanban Boards: Fetch Configuration (Columns & WIP)
elif board_type == 'kanban':
try:
config = jira_service.get_board_configuration(board_id)
board_data['configuration'] = config.model_dump(mode='json')
except Exception as e:
logger.warning(f"Could not fetch config for Kanban board {board_id}: {str(e)}")
board_data['configuration'] = {}
# C. Unknown/Other
else:
logger.info(f"Skipping detailed fetch for board type '{board_type}' (ID: {board_id})")
enhanced_boards.append(board_data)
full_data["boards"] = enhanced_boards
logger.info(f"Synced for user {account_id}: Assigned={len(assigned_issues)}, Projects={len(full_data['projects_data'])}, Boards={len(enhanced_boards)}")
# 5. Store data
success = supabase_service.upsert_jira_data(firebase_id, full_data)
if success:
return {"status": "processed", "user": firebase_id}
else:
raise HTTPException(status_code=500, detail="Failed to store data")
except Exception as e:
logger.error(f"Error syncing data for user {account_id}: {str(e)}")
# Return 200 to acknowledge webhook but log error
return {"status": "error", "detail": str(e)}
except Exception as e:
logger.error(f"Error processing webhook: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
|