Private/Invoke-DuneApiRequest.ps1
|
<#
.SYNOPSIS Send an authenticated HTTP request to the Dune API. .DESCRIPTION Core API request function used by all Dune cmdlets. Handles authentication headers (bearer token or web session), pagination via the ExtractItems switch, and error parsing. Automatically asserts that a valid session exists before sending requests. .PARAMETER Uri The relative API URI path (e.g. 'deployments', 'resources/123'). The base URL is prepended from the session. .PARAMETER Method The HTTP method. Valid values: GET, POST, PATCH, DELETE, PUT. Defaults to GET. .PARAMETER ExtractItems If set, enables automatic pagination and extracts the items array from paginated responses. Without this switch, the raw WebResponse is returned. .PARAMETER Body The request body object. Automatically serialized to JSON. .EXAMPLE PS> Invoke-DuneApiRequest -Uri "deployments" -ExtractItems Returns all deployments with automatic pagination. .EXAMPLE PS> Invoke-DuneApiRequest -Uri "resources/123" -Method PATCH -Body @{State = "Active"} Sends a PATCH request to update a resource. #> function Invoke-DuneApiRequest { [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$Uri, [Parameter()] [ValidateSet("GET", "POST", "PATCH", "DELETE", "PUT")] [string]$Method = 'GET', [Parameter()] [switch]$ExtractItems, # enables paging and extract items / will not return StatusCode [Parameter()] $Body # [Parameter(ParameterSetName='WebSession')] # [Microsoft.PowerShell.Commands.WebRequestSession]$WebSession, # [Parameter(ParameterSetName='Token')] # $Token, # [Parameter()] # [switch]$SkipAuth ) begin { Write-Debug "$($MyInvocation.MyCommand)|begin" Assert-DuneSession $Url = $("{0}/{1}" -f $DuneSession.DuneApiUrl, $Uri.Trim('/')) # $AuthUrl = "{0}{1}" -f $DuneSession.DuneApiUrl, "/auth/credentials" # if (-not ($SkipAuth) -and ($DuneSession.Type -eq 'Credential' -and -not $DuneSession.AuthSession -or ($DuneSession.AuthSession.Cookies.GetCookies($AuthUrl)).Expired -contains $True <#-or $DuneSession.SessionUrl -ne $ENV:SB_API_URL -or $DuneSessionUser -ne $ENV:SB_API_USER -or $DuneSessionPass -ne $ENV:SB_API_PASS -or $DuneSessionTenant -ne $ENV:SB_TENANT#>)) { # Invoke-DuneApiAuth # } $Headers = @{ "X-Tenant" = $DuneSession.Tenant "Accept" = "application/json" "Content-Type" = "application/json" } if ($DuneSession.Type -eq 'SocialLogin') { # Convert SecureString to plain text in a way compatible with PowerShell 5. if ($DuneSession.Token -is [System.Security.SecureString]) { $PlainToken = $null $convertCmd = Get-Command ConvertFrom-SecureString -ErrorAction SilentlyContinue if ($convertCmd -and $convertCmd.Parameters.Keys -contains 'AsPlainText') { $PlainToken = $DuneSession.Token | ConvertFrom-SecureString -AsPlainText } else { $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($DuneSession.Token) try { $PlainToken = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) } finally { if ($bstr) { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } } } else { $PlainToken = $DuneSession.Token } $Headers.Authorization = "Bearer $PlainToken" } } process { $PagingUrl = $Url $ResultObjects = @() do { $WebRequest = @{ Uri = $PagingUrl Method = $Method Headers = $Headers UseBasicParsing = $true } if ($DuneSession.Type -eq 'Credential') { $WebRequest.WebSession = $DuneSession.AuthSession } If ($DebugPreference -ne 'Continue') { $WebRequest.Verbose = $false } If ($Body) { $WebRequest.Body = $($Body | ConvertTo-Json -Compress -Depth 16) } if (([System.Uri]$DuneSession.DuneApiUrl).Host -eq 'localhost') { $WebRequest.SkipCertificateCheck = $true} Write-Debug "$($MyInvocation.MyCommand)|process|WebRequest: $($WebRequest | ConvertTo-Json -Depth 16 -Compress)" try { $Response = Invoke-WebRequest @WebRequest if ($Response.StatusCode -notlike "2??") { Write-Error ("{0} {1} returned statuscode: {2} ({3})" -f $Method, $Uri, $Response.StatusCode, $Response.StatusDescription) -ErrorAction Stop } if ($ExtractItems) { $Results = $Response.Content | ConvertFrom-Json if (($Results | Get-Member).Name -contains 'total' -and ($Results | Get-Member).Name -contains 'items') { $Total = $Results.total $Results = $Results.Items } $ResultObjects += $Results if ($Total -and ($ResultObjects.Count -ne $Total)) { Write-Debug ("$($MyInvocation.MyCommand)|process|Use paging for {0} {1}. (CurrentReturnedItems {2} / TotalReturnItems {3})" -f $Method, $Uri, $ResultObjects.Count, $Total) $PagingUrl = $PagingUrl | Add-UriQueryParam "skip=$($ResultObjects.Count)" } } else { return $Response } } catch { if ($_.Exception.Message) { if ($_.ErrorDetails.Message) { try { $ErrorDetailsMessage = $_.ErrorDetails.Message | ConvertFrom-Json throw ("{0} {1} returned error: {2} ({3})" -f $Method, $Uri, $_.Exception.Message, $ErrorDetailsMessage.responseStatus.message) } catch { $ErrorDetailsMessage = $_.ErrorDetails.Message throw ("{0} {1} returned error: {2} ({3})" -f $Method, $Uri, $_.Exception.Message, $ErrorDetailsMessage) } } else { throw ("{0} {1} returned error: {2}" -f $Method, $Uri, $_.Exception.Message) } } } } until ((-not $ExtractItems) -or (-not $Total) -or ($ResultObjects.Count -eq $Total)) return $ResultObjects } end { Write-Debug "$($MyInvocation.MyCommand)|end" } } |