Providers/PingOne/Private/Get-PingOneErrorDetail.ps1
|
function Get-PingOneErrorDetail { <# .SYNOPSIS Pulls the code, message and correlation id out of a failed PingOne response .DESCRIPTION PingOne answers a failure with a body rather than a useful status code. A 400 is returned for a malformed request, an unsupported grant type, a duplicate name and a field that failed validation alike, so branching on the status alone cannot tell a caller which of those happened. The body carries what matters: { "id": "...", "code": "INVALID_DATA", "message": "The request could not be completed.", "details": [ { "code": "INVALID_VALUE", "target": "name", "message": "..." } ] } The `id` is the correlation id, and it is the only thing Ping's support can act on, so it is kept even though nothing in this module reads it. The body is in ErrorDetails.Message on both editions: Invoke-TestWebRequest reads a failed response once, whichever way the running PowerShell hands it over, and puts it there. This function turns PingOne's shape into one line. .PARAMETER ErrorRecord The error record from the failed call. .OUTPUTS PSCustomObject with Status, Code, Message, Target, CorrelationId and Summary. Summary is the one-line form worth putting in an exception message. .EXAMPLE PS> try { Invoke-WebRequest ... } catch { Get-PingOneErrorDetail -ErrorRecord $_ } DESCRIPTION: Turns a failure into something worth printing OUTPUT: Status 400, Code INVALID_DATA, Target name, and a summary naming all three USE CASE: Called by Invoke-PingOneRequest on every failure .NOTES Author: Jeffrey Stuhr Blog: https://www.techbyjeff.net LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/ #> [CmdletBinding()] [OutputType([PSCustomObject])] param( [Parameter(Mandatory = $true)] [System.Management.Automation.ErrorRecord]$ErrorRecord ) $status = 0 if ($ErrorRecord.Exception.Response) { try { $status = [int]$ErrorRecord.Exception.Response.StatusCode } catch { $status = 0 } } # The body is in ErrorDetails on both editions: Invoke-TestWebRequest reads a failed response # once, with -SkipHttpErrorCheck on PowerShell 7 and from the response stream on Windows # PowerShell, and puts it there. Nothing here reads a stream. $raw = $null if ($ErrorRecord.ErrorDetails) { $raw = $ErrorRecord.ErrorDetails.Message } $code = $null $message = $null $target = $null $correlationId = $null if (-not [string]::IsNullOrWhiteSpace($raw)) { try { $parsed = $raw | ConvertFrom-Json $code = $parsed.code $message = $parsed.message $correlationId = $parsed.id if ($parsed.details) { $first = @($parsed.details)[0] $target = $first.target # The detail is more specific than the top-level message, which is usually the # useless "The request could not be completed." if ($first.message) { $message = $first.message } } } catch { Write-Verbose 'The error body was not JSON; using it as the message.' $message = ($raw -replace '\s+', ' ') } } if (-not $message) { $message = $ErrorRecord.Exception.Message } $summary = $code if ($target) { $summary = '{0} on {1}' -f $summary, $target } if ($summary) { $summary = '{0}: {1}' -f $summary, $message } else { $summary = $message } if ($correlationId) { $summary = '{0} (correlation id {1})' -f $summary, $correlationId } return [PSCustomObject]@{ Status = $status Code = $code Message = $message Target = $target CorrelationId = $correlationId Summary = $summary } } |