Private/Test-InforcerReportRunTerminal.ps1
|
function Test-InforcerReportRunTerminal { <# .SYNOPSIS Probes GET /beta/reports/runs/{id}/outputs once to determine if a run is terminal (Private helper). .DESCRIPTION Per API contract: * 200 → run is terminal; outputs are returned (downloadable) * 404 → run is not terminal yet (or doesn't exist — see note) Used as the polling probe by Get-InforcerReportRun -Wait. Returns a typed result rather than throwing on the "not yet" case, so polling loops can branch cleanly. Note: a 404 alone cannot distinguish "run pending" from "run-id invalid". This helper treats any 404 as "not terminal yet" — the caller's timeout loop is what catches a bogus run id (it times out instead of completing). Run-id format validation happens client-side via [guid]::TryParse before this is called. .PARAMETER RunId The run identifier (GUID) returned from POST /beta/reports/runs. .OUTPUTS PSCustomObject with members: RunId — the run identifier (string form of the guid) IsTerminal — boolean Outputs — the parsed outputs array (when IsTerminal=$true), else $null CorrelationId — x-correlation-id of this probe, or $null StatusCode — raw HTTP status code (int) #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory = $true)] [guid]$RunId ) if (-not (Test-InforcerSession)) { Write-Error -Message 'Not connected. Run Connect-Inforcer first.' ` -ErrorId 'NotConnected' -Category ConnectionError return } $apiKey = ConvertFrom-InforcerSecureString -SecureString $script:InforcerSession.ApiKey if ([string]::IsNullOrWhiteSpace($apiKey)) { Write-Error -Message 'API key is empty or invalid. Please reconnect.' ` -ErrorId 'EmptyApiKey' -Category AuthenticationError return } $uri = $script:InforcerSession.BaseUrl + ('/beta/reports/runs/{0}/outputs' -f $RunId) $headers = @{ 'Inf-Api-Key' = $apiKey 'Accept' = 'application/json' } Write-Verbose "Probing terminal state: GET $uri" try { $response = Invoke-WebRequest -Uri $uri -Method GET -Headers $headers -SkipHttpErrorCheck -UseBasicParsing -ErrorAction Stop } catch { $msg = Protect-InforcerApiKeyInText -Text $_.Exception.Message -ApiKey $apiKey Write-Error -Message "Failed to probe run terminal state: $msg" ` -ErrorId 'ProbeFailed' -Category ConnectionError return } $statusCode = [int]$response.StatusCode $correlationId = Get-InforcerHeaderValue -Headers $response.Headers -Name 'x-correlation-id' if ($correlationId) { Write-Verbose "x-correlation-id: $correlationId" } if ($statusCode -eq 200) { # Response shape (confirmed): { data: { outputs: [...] }, success: true, message, errors } # Unwrap: response → .data → .outputs (the array of output records). $outputs = @() if ($response.Content) { try { $body = $response.Content | ConvertFrom-Json -Depth 100 -ErrorAction Stop $unwrapped = $body $dataProp = $body.PSObject.Properties['data'] if ($dataProp -and $null -ne $dataProp.Value) { $unwrapped = $dataProp.Value } if ($unwrapped -is [PSObject]) { $outputsProp = $unwrapped.PSObject.Properties['outputs'] if ($outputsProp -and $null -ne $outputsProp.Value) { $outputs = @($outputsProp.Value) } elseif ($unwrapped -is [array]) { $outputs = @($unwrapped) } } elseif ($unwrapped -is [array]) { $outputs = @($unwrapped) } } catch { Write-Verbose '200 response was not JSON-parseable; returning empty outputs.' } } return [PSCustomObject]@{ RunId = $RunId.ToString() IsTerminal = $true Outputs = $outputs CorrelationId = $correlationId StatusCode = $statusCode } } if ($statusCode -eq 404) { return [PSCustomObject]@{ RunId = $RunId.ToString() IsTerminal = $false Outputs = $null CorrelationId = $correlationId StatusCode = $statusCode } } # Any other status is an error worth surfacing — parse the body for a useful message. $detail = $response.Content if ($detail) { try { $json = $response.Content | ConvertFrom-Json -ErrorAction Stop $msgProp = $json.PSObject.Properties['message'] if ($msgProp) { $detail = $msgProp.Value -as [string] } } catch { Write-Verbose 'Error response body was not JSON; using raw content.' } } if ([string]::IsNullOrWhiteSpace($detail)) { $detail = "HTTP $statusCode" } $errMsg = "Failed to probe run '$RunId': $detail" if ($correlationId) { $errMsg = "$errMsg [correlation-id: $correlationId]" } Write-Error -Message $errMsg -ErrorId "ProbeFailed_$statusCode" -Category ConnectionError } |