Modules/businessdev.ALbuild.OnPrem/Private/Invoke-BcAutomationRequest.ps1
|
function Invoke-BcAutomationRequest { <# .SYNOPSIS Calls the Business Central automation API and surfaces the real reason when it fails. .DESCRIPTION Invoke-RestMethod reports only the status line - "The remote server returned an error: (400) Bad Request." - while the automation API returns a JSON body naming the actual problem (a missing dependency, a schema change that needs Force Sync, an app that is not signed, ...). Without that body a failed per-tenant publish is undiagnosable from the release log. This wrapper reads the response body across Windows PowerShell 5.1 (WebException response stream) and PowerShell 7+ (body pre-read into ErrorDetails.Message) and appends it to the thrown message, together with the operation being performed and the URI. It mirrors what Get-BcMarketplaceErrorDetail does for the Partner Center ingestion API. .PARAMETER Uri The full automation API URI. .PARAMETER Method The HTTP method. .PARAMETER Headers Request headers (Authorization, If-Match, Content-Type, ...). .PARAMETER Body Optional request body (JSON string or byte array). .PARAMETER Operation Short human description used in the error message, e.g. "create the extension upload record". .OUTPUTS The deserialised API response. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string] $Uri, [Parameter(Mandatory)] [ValidateSet('Get', 'Post', 'Patch', 'Put', 'Delete')] [string] $Method, [Parameter(Mandatory)] [hashtable] $Headers, [object] $Body, [Parameter(Mandatory)] [string] $Operation ) $params = @{ Uri = $Uri; Method = $Method; Headers = $Headers; ErrorAction = 'Stop' } if ($null -ne $Body) { $params['Body'] = $Body } try { return Invoke-RestMethod @params } catch { $status = 0 $responseProperty = $_.Exception.PSObject.Properties['Response'] if ($responseProperty -and $responseProperty.Value) { try { $status = [int] $responseProperty.Value.StatusCode } catch { $status = 0 } } if (-not $status -and "$($_.Exception.Message)" -match '\((\d{3})\)') { $status = [int] $Matches[1] } $detail = $null if ($_.ErrorDetails -and $_.ErrorDetails.Message) { $detail = "$($_.ErrorDetails.Message)" } elseif ($responseProperty -and $responseProperty.Value) { try { $stream = $responseProperty.Value.GetResponseStream() if ($stream) { $reader = New-Object System.IO.StreamReader($stream) try { if ($reader.BaseStream.CanSeek) { $reader.BaseStream.Position = 0 } $detail = $reader.ReadToEnd() } finally { $reader.Close() } } } catch { Write-Verbose "Could not read the error response body: $($_.Exception.Message)" } } $statusText = if ($status) { "HTTP $status" } else { 'the request' } throw ("Failed to $Operation ($statusText): $($_.Exception.Message)" + $(if ($detail) { "`nBusiness Central response: $($detail.Trim())" } else { "`n(The API returned no response body.)" }) + "`nRequest: $Method $Uri") } } |