Private/Invoke-KB4Request.ps1
|
function Invoke-KB4Request { [CmdletBinding()] param( [Parameter()] [ValidateSet('GET', 'POST', 'PUT', 'PATCH', 'DELETE')] [string] $Method = 'GET', [Parameter(Mandatory)] [string] $Path, [Parameter()] [hashtable] $Query = @{}, [Parameter()] [AllowNull()] [object] $Body, [Parameter()] [ValidateRange(0, 10)] [int] $MaximumRetryCount = 3 ) $headers = Get-KB4AuthorizationHeader $uri = Resolve-KB4Uri -Path $Path -Query $Query $attempt = 0 do { $attempt++ # Rate limiting is centralized so all public commands behave consistently. Invoke-KB4RateLimitDelay $responseHeaders = $null $statusCode = $null $requestParameters = @{ Method = $Method Uri = $uri Headers = $headers ResponseHeadersVariable = 'responseHeaders' StatusCodeVariable = 'statusCode' SkipHttpErrorCheck = $true } if ($null -ne $Body) { $requestParameters['Body'] = $Body $requestParameters['ContentType'] = 'application/json' } if ($null -ne $script:KB4InvokeRestMethodOverride) { # Unit tests inject a fake transport here; production uses Invoke-RestMethod. $transportResponse = & $script:KB4InvokeRestMethodOverride $requestParameters $responseBody = $transportResponse.Body $responseHeaders = $transportResponse.Headers $statusCode = $transportResponse.StatusCode } else { $responseBody = Invoke-RestMethod @requestParameters } if ($statusCode -lt 400) { return [pscustomobject] @{ Body = $responseBody Headers = $responseHeaders StatusCode = $statusCode RequestId = Get-KB4HeaderValue -Headers $responseHeaders -Name @('X-Request-Id', 'x-request-id') Uri = $uri } } $retryable = $statusCode -eq 429 -or ($statusCode -ge 500 -and $statusCode -lt 600) if ($retryable -and $attempt -le $MaximumRetryCount) { $retryAfter = Get-KB4HeaderValue -Headers $responseHeaders -Name @('Retry-After', 'retry-after') $delaySeconds = 1 # Honor server-provided retry guidance before falling back to backoff. if ($retryAfter -as [int]) { $delaySeconds = [int] $retryAfter } else { $delaySeconds = [Math]::Min([Math]::Pow(2, $attempt - 1), 30) } Start-Sleep -Seconds $delaySeconds continue } $message = "KnowBe4 Reporting API request failed with HTTP status $statusCode." $requestId = Get-KB4HeaderValue -Headers $responseHeaders -Name @('X-Request-Id', 'x-request-id') if (-not [string]::IsNullOrWhiteSpace([string] $requestId)) { $message = "$message Request ID: $requestId." } $errorRecord = [System.Management.Automation.ErrorRecord]::new( [System.Exception]::new($message), 'KB4ReportingApiRequestFailed', [System.Management.Automation.ErrorCategory]::InvalidOperation, $uri ) $PSCmdlet.ThrowTerminatingError($errorRecord) } while ($attempt -le $MaximumRetryCount) } |