diff --git a/sentry-api-client/Public/Get-SentryEventAttachments.ps1 b/sentry-api-client/Public/Get-SentryEventAttachments.ps1 new file mode 100644 index 0000000..2d761c4 --- /dev/null +++ b/sentry-api-client/Public/Get-SentryEventAttachments.ps1 @@ -0,0 +1,31 @@ +function Get-SentryEventAttachments { + <# + .SYNOPSIS + Retrieves attachments for a specific event from Sentry. + + .DESCRIPTION + Fetches attachment metadata (name, size, type, content type) for a specific Sentry event. + Does not download attachment content. Automatically removes hyphens from GUID-formatted event IDs. + + .PARAMETER EventId + The unique identifier of the event whose attachments to retrieve. + + .EXAMPLE + Get-SentryEventAttachments -EventId "abc123def456" + # Returns an array of attachment objects for the event + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$EventId + ) + + # Remove hyphens from GUID-formatted event IDs + $EventId = $EventId -replace '-', '' + + $Uri = Get-SentryProjectUrl -Resource "events/$EventId/attachments/" + + # The API returns a top-level JSON array. ConvertFrom-Json -AsHashtable unwraps + # single-element arrays into a hashtable, so wrap in @() to ensure array output. + return , @(Invoke-SentryApiRequest -Uri $Uri -Method 'GET') +} diff --git a/sentry-api-client/README.md b/sentry-api-client/README.md index 4fdbccd..a2d2a76 100644 --- a/sentry-api-client/README.md +++ b/sentry-api-client/README.md @@ -18,6 +18,9 @@ Connect-SentryApi -ApiToken "your-api-token" -Organization "your-org" -Project " # Get specific event Get-SentryEvent -EventId "123456" +# Get event attachments +Get-SentryEventAttachments -EventId "123456" + # Find events by tag Get-SentryEventsByTag -TagName 'environment' -TagValue 'production' @@ -74,6 +77,15 @@ Clears the current Sentry API connection and configuration. Retrieves a specific event from Sentry by its ID. +### Get-SentryEventAttachments + +Retrieves attachment metadata (name, size, type, content type) for a specific event. Does not download attachment content. Returns an array of attachment metadata objects. + +```powershell +# Get attachments for an event +Get-SentryEventAttachments -EventId "123456" +``` + ### Get-SentryEventsByTag Retrieves events filtered by a specific tag name and value. diff --git a/sentry-api-client/SentryApiClient.psd1 b/sentry-api-client/SentryApiClient.psd1 index d04ee5b..cda7ffe 100644 --- a/sentry-api-client/SentryApiClient.psd1 +++ b/sentry-api-client/SentryApiClient.psd1 @@ -34,6 +34,7 @@ 'Find-SentryEventByTag', 'Get-SentryCLI', 'Get-SentryEvent', + 'Get-SentryEventAttachments', 'Get-SentryEventsByTag', 'Get-SentryLogs', 'Get-SentryLogsByAttribute', diff --git a/sentry-api-client/Tests/SentryApiClient.Tests.ps1 b/sentry-api-client/Tests/SentryApiClient.Tests.ps1 index 63a4dc8..792208a 100644 --- a/sentry-api-client/Tests/SentryApiClient.Tests.ps1 +++ b/sentry-api-client/Tests/SentryApiClient.Tests.ps1 @@ -18,6 +18,7 @@ Describe 'SentryApiClient Module' { 'Connect-SentryApi', 'Disconnect-SentryApi', 'Get-SentryEvent', + 'Get-SentryEventAttachments', 'Find-SentryEventByTag', 'Get-SentryEventsByTag', 'Invoke-SentryCLI', @@ -91,6 +92,31 @@ Describe 'SentryApiClient Module' { # Invoke-WebRequest returns an object with a Content property containing JSON # Order matters - most specific patterns first switch -Regex ($Uri) { + '/events/\w+/attachments/' { + $responseData = @( + @{ + id = '111' + event_id = '12345678901234567890123456789012' + type = 'event.attachment' + name = 'hello.txt' + mimetype = 'text/plain' + size = 14 + headers = @{ 'Content-Type' = 'text/plain' } + sha1 = 'abc123' + }, + @{ + id = '222' + event_id = '12345678901234567890123456789012' + type = 'event.attachment' + name = 'config.txt' + mimetype = 'application/octet-stream' + size = 42 + headers = @{ 'Content-Type' = 'application/octet-stream' } + sha1 = 'def456' + } + ) + return @{ Content = ($responseData | ConvertTo-Json -Depth 10) } + } '/issues/[^/]+/events/' { # Handle issues/{id}/events/ endpoint - most specific first $responseData = @( @@ -271,6 +297,60 @@ Describe 'SentryApiClient Module' { Assert-MockCalled -ModuleName SentryApiClient Invoke-WebRequest -Times 3 } } + + Context 'Get-SentryEventAttachments' { + It 'Should retrieve attachments for an event' { + $eventId = '12345678901234567890123456789012' + $result = Get-SentryEventAttachments -EventId $eventId + + $result | Should -Not -BeNullOrEmpty + $result | Should -HaveCount 2 + $result[0].name | Should -Be 'hello.txt' + $result[1].name | Should -Be 'config.txt' + } + + It 'Should remove hyphens from GUID-formatted event ID' { + $guidEventId = '12345678-9012-3456-7890-123456789012' + + Get-SentryEventAttachments -EventId $guidEventId + + Assert-MockCalled -ModuleName SentryApiClient Invoke-WebRequest -ParameterFilter { + $Uri -like '*events/12345678901234567890123456789012/attachments/*' + } + } + + It 'Should construct correct API URL' { + $eventId = '12345678901234567890123456789012' + + Get-SentryEventAttachments -EventId $eventId + + Assert-MockCalled -ModuleName SentryApiClient Invoke-WebRequest -ParameterFilter { + $Uri -match 'https://sentry.io/api/0/projects/test-org/test-project/events/\w+/attachments/' + } + } + + It 'Should return array even with single attachment' { + Mock -ModuleName SentryApiClient Invoke-WebRequest { + $responseData = @( + @{ + id = '333' + event_id = 'single' + type = 'event.attachment' + name = 'only.txt' + size = 5 + headers = @{ 'Content-Type' = 'text/plain' } + } + ) + return @{ Content = ($responseData | ConvertTo-Json -Depth 10) } + } + + $result = Get-SentryEventAttachments -EventId 'single' + + $result -is [Array] | Should -BeTrue + $result | Should -HaveCount 1 + $result[0].name | Should -Be 'only.txt' + } + } } Context 'Error Handling' { diff --git a/utils/Integration.TestUtils.psm1 b/utils/Integration.TestUtils.psm1 index db0ea7e..6ef4dc6 100644 --- a/utils/Integration.TestUtils.psm1 +++ b/utils/Integration.TestUtils.psm1 @@ -455,5 +455,64 @@ function Get-SentryTestTransaction { throw "Transaction with trace $TraceId not found in Sentry within $TimeoutSeconds seconds: $lastError" } +function Get-SentryTestEventAttachments { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$EventId, + + [Parameter()] + [int]$ExpectedCount = 1, + + [Parameter()] + [int]$TimeoutSeconds = 120 + ) + + Write-Host "Fetching Sentry event attachments for event: $EventId" -ForegroundColor Yellow + $progressActivity = "Waiting for attachments on event $EventId" + + $startTime = Get-Date + $endTime = $startTime.AddSeconds($TimeoutSeconds) + $lastError = $null + $elapsedSeconds = 0 + + try { + do { + $attachments = @() + $elapsedSeconds = [int]((Get-Date) - $startTime).TotalSeconds + $percentComplete = [math]::Min(100, ($elapsedSeconds / $TimeoutSeconds) * 100) + + Write-Progress -Activity $progressActivity -Status "Elapsed: $elapsedSeconds/$TimeoutSeconds seconds" -PercentComplete $percentComplete + + try { + $response = Get-SentryEventAttachments -EventId $EventId + if ($response.Count -ge $ExpectedCount) { + $attachments = $response + } + } catch { + $lastError = $_.Exception.Message + Write-Debug "Attachments for event $EventId not found yet: $lastError" + } + + if ($attachments.Count -ge $ExpectedCount) { + Write-Host "Found $($attachments.Count) attachment(s) for event $EventId" -ForegroundColor Green + + $attachmentsJson = $attachments | ConvertTo-Json -Depth 10 + $attachmentsJson | Out-File -FilePath (Get-OutputFilePath "attachments-$EventId.json") + + return , @($attachments) + } + + Start-Sleep -Milliseconds 500 + $currentTime = Get-Date + } while ($currentTime -lt $endTime) + } finally { + Write-Progress -Activity $progressActivity -Completed + } + + $foundCount = if ($attachments) { $attachments.Count } else { 0 } + throw "Expected at least $ExpectedCount attachment(s) for event $EventId but found $foundCount within $TimeoutSeconds seconds. Last error: $lastError" +} + # Export module functions -Export-ModuleMember -Function Invoke-CMakeConfigure, Invoke-CMakeBuild, Set-OutputDir, Get-OutputFilePath, Get-EventIds, Get-SentryTestEvent, Get-SentryTestLog, Get-SentryTestMetric, Get-SentryTestTransaction, Get-PackageAumid +Export-ModuleMember -Function Invoke-CMakeConfigure, Invoke-CMakeBuild, Set-OutputDir, Get-OutputFilePath, Get-EventIds, Get-SentryTestEvent, Get-SentryTestEventAttachments, Get-SentryTestLog, Get-SentryTestMetric, Get-SentryTestTransaction, Get-PackageAumid