workflows/kickstart-via-jira/systems/mcp/tools/atlassian-download/script.ps1
|
function Invoke-AtlassianDownload { param([hashtable]$Arguments) $jiraKey = $Arguments['jira_key'] $targetDir = $Arguments['target_dir'] if (-not $jiraKey) { throw "jira_key is required" } # --------------------------------------------------------------------------- # Load .env.local for Atlassian credentials # --------------------------------------------------------------------------- $envLocal = Join-Path $global:DotbotProjectRoot ".env.local" if (Test-Path $envLocal) { Get-Content $envLocal | ForEach-Object { if ($_ -match '^\s*([^#][^=]+)=(.*)$') { [Environment]::SetEnvironmentVariable($matches[1].Trim(), $matches[2].Trim(), "Process") } } } $email = $env:ATLASSIAN_EMAIL $apiToken = $env:ATLASSIAN_API_TOKEN $cloudId = $env:ATLASSIAN_CLOUD_ID # Auto-resolve site URL to Cloud ID UUID if ($cloudId -and $cloudId -match '\.atlassian\.net') { $siteUrl = ($cloudId -replace '/+$', '') if ($siteUrl -notmatch '^https?://') { $siteUrl = "https://$siteUrl" } try { $tenantInfo = Invoke-RestMethod -Uri "$siteUrl/_edge/tenant_info" -ErrorAction Stop $cloudId = $tenantInfo.cloudId $env:ATLASSIAN_CLOUD_ID = $cloudId # cache for subsequent calls } catch { throw "ATLASSIAN_CLOUD_ID looks like a site URL but could not resolve via _edge/tenant_info: $_`nSet ATLASSIAN_CLOUD_ID to the UUID directly (find it at $siteUrl/_edge/tenant_info)" } } if (-not $email) { throw "ATLASSIAN_EMAIL not set in .env.local" } if (-not $apiToken) { throw "ATLASSIAN_API_TOKEN not set in .env.local" } if (-not $cloudId) { throw "ATLASSIAN_CLOUD_ID not set in .env.local" } # --------------------------------------------------------------------------- # Determine target directory # --------------------------------------------------------------------------- if (-not $targetDir) { $targetDir = ".bot\workspace\product\briefing\docs" } $docsPath = Join-Path $global:DotbotProjectRoot $targetDir if (-not (Test-Path $docsPath)) { New-Item -Path $docsPath -ItemType Directory -Force | Out-Null } # --------------------------------------------------------------------------- # Auth header for Atlassian REST API # --------------------------------------------------------------------------- $authBytes = [System.Text.Encoding]::ASCII.GetBytes("${email}:${apiToken}") $authHeader = "Basic " + [System.Convert]::ToBase64String($authBytes) $headers = @{ "Authorization" = $authHeader "Accept" = "application/json" } $baseUrl = "https://api.atlassian.com/ex/jira/$cloudId/rest/api/3" $confluenceBaseUrl = "https://api.atlassian.com/ex/confluence/$cloudId" $downloadedFiles = @() # --------------------------------------------------------------------------- # Helper: Download a single attachment # --------------------------------------------------------------------------- function Download-Attachment { param( [string]$Url, [string]$FileName, [string]$IssueKey, [string]$Topic, [string]$Source ) $safeTopic = $Topic -replace '[^\w\-]', '_' $safeFile = $FileName -replace '[^\w\-\.]', '_' $destName = "${IssueKey}_${safeTopic}_${safeFile}" $destPath = Join-Path $docsPath $destName try { Invoke-WebRequest -Uri $Url -Headers $headers -OutFile $destPath -ErrorAction Stop $fileInfo = Get-Item $destPath return @{ path = "$targetDir/$destName" original_name = $FileName source = $Source size = $fileInfo.Length content_type = switch -Wildcard ([System.IO.Path]::GetExtension($destPath).ToLower()) { '.pdf' { 'application/pdf' } '.png' { 'image/png' } '.jpg' { 'image/jpeg' } '.jpeg' { 'image/jpeg' } '.gif' { 'image/gif' } '.svg' { 'image/svg+xml' } '.doc' { 'application/msword' } '.docx' { 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' } '.xls' { 'application/vnd.ms-excel' } '.xlsx' { 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } '.ppt' { 'application/vnd.ms-powerpoint' } '.pptx' { 'application/vnd.openxmlformats-officedocument.presentationml.presentation' } '.msg' { 'application/vnd.ms-outlook' } '.zip' { 'application/zip' } '.json' { 'application/json' } '.txt' { 'text/plain' } '.csv' { 'text/csv' } '.html' { 'text/html' } '.md' { 'text/markdown' } default { 'application/octet-stream' } } issue_key = $IssueKey } } catch { Write-Warning "Failed to download $FileName from $Source : $_" return $null } } # --------------------------------------------------------------------------- # 1. Get attachments from the main issue # --------------------------------------------------------------------------- try { $issueResp = Invoke-RestMethod -Uri "$baseUrl/issue/${jiraKey}?fields=attachment,issuelinks,summary" ` -Headers $headers -ErrorAction Stop if ($issueResp.fields.attachment) { foreach ($att in $issueResp.fields.attachment) { $result = Download-Attachment ` -Url $att.content -FileName $att.filename ` -IssueKey $jiraKey -Topic "Main" -Source "jira-attachment" if ($result) { $downloadedFiles += $result } } } } catch { Write-Warning "Failed to fetch main issue $jiraKey : $_" } # --------------------------------------------------------------------------- # 2. Get attachments from child issues # --------------------------------------------------------------------------- try { $jql = [System.Uri]::EscapeDataString("parent = $jiraKey") $childResp = Invoke-RestMethod -Method Post ` -Uri "$baseUrl/search/jql" ` -Headers ($headers + @{ "Content-Type" = "application/json" }) ` -Body (@{ jql = "parent = $jiraKey"; fields = @("key","summary","attachment"); maxResults = 50 } | ConvertTo-Json) ` -ErrorAction Stop foreach ($child in $childResp.issues) { if ($child.fields.attachment) { $childTopic = ($child.fields.summary -replace '[^\w\s\-]', '') -replace '\s+', '_' if ($childTopic.Length -gt 40) { $childTopic = $childTopic.Substring(0, 40) } foreach ($att in $child.fields.attachment) { $result = Download-Attachment ` -Url $att.content -FileName $att.filename ` -IssueKey $child.key -Topic $childTopic -Source "jira-child-attachment" if ($result) { $downloadedFiles += $result } } } } } catch { Write-Warning "Failed to fetch child issues for $jiraKey : $_" } # --------------------------------------------------------------------------- # 3. Get attachments from linked Confluence pages # --------------------------------------------------------------------------- try { $remoteLinks = Invoke-RestMethod ` -Uri "$baseUrl/issue/${jiraKey}/remotelink" ` -Headers $headers -ErrorAction Stop $confluencePageIds = @() foreach ($link in $remoteLinks) { $linkUrl = $link.object.url if ($linkUrl -match '/wiki/.*?/pages/(\d+)') { $confluencePageIds += $matches[1] } } # Also check issue links for Confluence mentions if ($issueResp.fields.issuelinks) { foreach ($link in $issueResp.fields.issuelinks) { $linkedKey = if ($link.outwardIssue) { $link.outwardIssue.key } elseif ($link.inwardIssue) { $link.inwardIssue.key } # We only process Confluence remote links, not Jira-to-Jira links here } } foreach ($pageId in $confluencePageIds) { try { $pageResp = Invoke-RestMethod ` -Uri "$confluenceBaseUrl/wiki/api/v2/pages/${pageId}?body-format=storage" ` -Headers $headers -ErrorAction Stop $pageTitle = ($pageResp.title -replace '[^\w\s\-]', '') -replace '\s+', '_' if ($pageTitle.Length -gt 40) { $pageTitle = $pageTitle.Substring(0, 40) } # Get page attachments $attResp = Invoke-RestMethod ` -Uri "$confluenceBaseUrl/wiki/api/v2/pages/${pageId}/attachments" ` -Headers $headers -ErrorAction Stop foreach ($att in $attResp.results) { $downloadUrl = "$confluenceBaseUrl/wiki/rest/api/content/$($att.id)/download" $result = Download-Attachment ` -Url $downloadUrl -FileName $att.title ` -IssueKey $jiraKey -Topic $pageTitle -Source "confluence-attachment" if ($result) { $downloadedFiles += $result } } } catch { Write-Warning "Failed to fetch Confluence page $pageId : $_" } } } catch { Write-Warning "Failed to fetch remote links for $jiraKey : $_" } # --------------------------------------------------------------------------- # Return manifest # --------------------------------------------------------------------------- return @{ success = $true files = $downloadedFiles count = $downloadedFiles.Count message = "Downloaded $($downloadedFiles.Count) file(s) to $targetDir" } } |