Private/Invoke-MyWebApiRequest.ps1
|
function Invoke-MyWebApiRequest { # Single choke point for every REST cmdlet: auth, URL build, envelope unwrap, paging. [CmdletBinding()] param( [Parameter(Mandatory)][string] $Method, [Parameter(Mandatory)][string] $Path, [hashtable] $Query, [object] $Body, [string] $TradePlatform, [Nullable[guid]] $CacheId, [int] $CacheTimeout, [string] $IdempotencyKey, [switch] $All, [object] $Connection ) $ctx = Resolve-MyWebApiContext -Connection $Connection $safeMethod = $Method.ToUpperInvariant() -in @('GET', 'HEAD', 'OPTIONS') $configuredRetries = Get-MyWebApiContextProperty -Context $ctx -Name 'MaxGetRetries' $maxRetries = if ($safeMethod -and $null -ne $configuredRetries) { [Math]::Min([int]$configuredRetries, 3) } else { 0 } # Resolve {tradePlatform} from the explicit connection or the session default. if ($Path -like '*{tradePlatform}*') { $defaultPlatform = Get-MyWebApiContextProperty -Context $ctx -Name 'DefaultTradePlatform' $tp = if ($TradePlatform) { $TradePlatform } else { $defaultPlatform } if (-not $tp) { throw 'No trade platform: pass -TradePlatform or set -DefaultTradePlatform on Connect-MyWebApi.' } $Path = $Path.Replace('{tradePlatform}', [uri]::EscapeDataString($tp)) } $token = Get-MyWebApiToken -Connection $ctx $headers = @{ Authorization = "Bearer $token"; Accept = 'application/json' } if ($IdempotencyKey) { $headers['Idempotency-Key'] = $IdempotencyKey } $q = @{} if ($Query) { foreach ($k in $Query.Keys) { if ($null -ne $Query[$k]) { $q[$k] = $Query[$k] } } } if ($CacheId) { $q['cacheId'] = $CacheId.ToString() } if ($CacheTimeout) { $q['cacheTimeout'] = $CacheTimeout } $accumulated = [System.Collections.Generic.List[object]]::new() $cursor = $null $more = $false do { if ($All -and $cursor) { $q['cursor'] = $cursor } $qsParts = [System.Collections.Generic.List[string]]::new() foreach ($kv in $q.GetEnumerator()) { foreach ($v in @($kv.Value)) { $qsParts.Add(("{0}={1}" -f [uri]::EscapeDataString([string]$kv.Key), [uri]::EscapeDataString([string]$v))) } } $qs = $qsParts -join '&' $baseUrl = [string](Get-MyWebApiContextProperty -Context $ctx -Name 'BaseUrl') $uri = "$baseUrl$Path" if ($qs) { $uri = "$uri`?$qs" } $irmArgs = @{ Context = $ctx Method = $Method Uri = $uri Headers = $headers JsonBody = $Body } # GET/HEAD/OPTIONS are safe to retry with a bounded policy. Writes are always # one-shot, regardless of Idempotency-Key: a lost response is an ambiguous outcome. $attempt = 0 while ($true) { try { Assert-MyWebApiNotCancelled -Context $ctx $resp = Invoke-MyWebApiHttpJson @irmArgs break } catch { $statusCode = $null $responseProperty = $_.Exception.PSObject.Properties['Response'] if ($responseProperty -and $responseProperty.Value) { try { $statusCode = [int]$responseProperty.Value.StatusCode } catch { $statusCode = $null } } if ($null -eq $statusCode) { $dataStatus = $_.Exception.Data['StatusCode'] if ($null -ne $dataStatus) { $statusCode = [int]$dataStatus } } $retryableStatus = $statusCode -in @(408, 425, 429, 500, 502, 503, 504) $retryable = $safeMethod -and $attempt -lt $maxRetries -and ($retryableStatus -or $null -eq $statusCode) if (-not $retryable) { throw } $delayMs = [Math]::Min(250 * [Math]::Pow(2, $attempt), 2000) if ((Get-MyWebApiCancellationToken -Context $ctx).WaitHandle.WaitOne([int]$delayMs)) { throw [System.OperationCanceledException]::new('The WebAPI session operation was cancelled.') } $attempt++ } } $errObj = Get-MyWebApiContextProperty -Context $resp -Name 'error' if ($errObj) { $metaObj = Get-MyWebApiContextProperty -Context $resp -Name 'meta' $activityId = Get-MyWebApiContextProperty -Context $metaObj -Name 'activityId' $code = Get-MyWebApiContextProperty -Context $errObj -Name 'code' $errMessage = Get-MyWebApiContextProperty -Context $errObj -Name 'message' $managerCode = Get-MyWebApiContextProperty -Context $errObj -Name 'managerCode' $msg = if ($errMessage) { $errMessage } else { "v2 error: $code" } $rec = [System.Management.Automation.ErrorRecord]::new( [System.Exception]::new("$msg (code=$code; activityId=$activityId; managerCode=$managerCode)"), "MyWebApiError,$code", [System.Management.Automation.ErrorCategory]::InvalidOperation, $null) $PSCmdlet.ThrowTerminatingError($rec) } $dataObj = Get-MyWebApiContextProperty -Context $resp -Name 'data' if ($All) { if ($null -ne $dataObj) { foreach ($item in @($dataObj)) { $accumulated.Add($item) } } $metaObj = Get-MyWebApiContextProperty -Context $resp -Name 'meta' $paging = Get-MyWebApiContextProperty -Context $metaObj -Name 'paging' $cursor = Get-MyWebApiContextProperty -Context $paging -Name 'nextCursor' $more = [bool]($paging -and (Get-MyWebApiContextProperty -Context $paging -Name 'hasMore') -and $cursor) } else { return $dataObj } } while ($All -and $more) return $accumulated.ToArray() } |