Private/Invoke-JuribaAppRRestMethod.ps1
|
function Invoke-JuribaAppRRestMethod { <# .SYNOPSIS Internal helper that wraps Invoke-RestMethod with standard AppR authentication and error handling. .DESCRIPTION Constructs the full URI, adds the x-api-key header, and invokes the REST method. Supports all HTTP methods and handles common error patterns consistently. This function is not exported and is used internally by public cmdlets. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$Instance, [Parameter(Mandatory = $true)] [string]$APIKey, [Parameter(Mandatory = $true)] [string]$Uri, [Parameter(Mandatory = $false)] [ValidateSet('GET', 'POST', 'PUT', 'PATCH', 'DELETE')] [string]$Method = 'GET', [Parameter(Mandatory = $false)] [object]$Body, [Parameter(Mandatory = $false)] [string]$ContentType = 'application/json', [Parameter(Mandatory = $false)] [string]$OutFile ) # Build full URI $fullUri = "{0}/{1}" -f $Instance.TrimEnd('/'), $Uri.TrimStart('/') # Build headers — Accept: application/json is critical; without it many # endpoints return the SPA HTML instead of JSON data. $headers = @{ "x-api-key" = $APIKey "Accept" = "application/json" } # Build splat for Invoke-RestMethod $splat = @{ Uri = $fullUri Method = $Method Headers = $headers ContentType = $ContentType } if ($Body) { if ($Body -is [string]) { $splat['Body'] = $Body } else { $splat['Body'] = $Body | ConvertTo-Json -Depth 10 } Write-Verbose "Request body: $($splat['Body'])" } if ($OutFile) { $splat['OutFile'] = $OutFile } Write-Verbose "$Method $fullUri" try { $response = Invoke-RestMethod @splat return $response } catch { $statusCode = $null $errorMessage = $_.Exception.Message $errorBody = $null # PowerShell 7: ErrorDetails.Message contains the response body if ($_.ErrorDetails -and $_.ErrorDetails.Message) { $errorBody = $_.ErrorDetails.Message } if ($_.Exception.Response) { $statusCode = [int]$_.Exception.Response.StatusCode # PowerShell 5.1 fallback: read from response stream if (-not $errorBody) { try { $errorStream = $_.Exception.Response.GetResponseStream() $reader = New-Object System.IO.StreamReader($errorStream) $errorBody = $reader.ReadToEnd() $reader.Close() } catch { Write-Verbose "Could not read error body from response stream: $($_.Exception.Message)" } } } if ($errorBody) { $errorMessage = "{0} - {1}" -f $errorMessage, $errorBody } Write-Verbose "API Error [$statusCode]: $errorMessage" # Use throw (not Write-Error) so callers always see a terminating error switch ($statusCode) { 401 { throw "Authentication failed. Please check your API key. $errorMessage" } 403 { throw "Access denied. You do not have permission for this operation. $errorMessage" } 404 { throw "Resource not found. $errorMessage" } default { throw "API request failed: $errorMessage" } } } } |