Public/Web/Invoke-ApiListPagedWithContinuationToken.ps1
function Invoke-ApiListPagedWithContinuationToken { <# .SYNOPSIS Calls web API returning paged list of records using continuation token pagination. .DESCRIPTION Calls web API returning paged list of records using continuation token pagination. This is used for APIs that use the x-ms-continuationtoken header for pagination instead of $skip and $top query parameters. The function iterates through all pages by following the continuation token returned in the response header until no more pages are available. .PARAMETER ApiCredential Credential to use for authentication. If not specified, $global:AzureDevOpsApi_ApiCredential (set by Set-AzureDevopsVariables) is used. .PARAMETER ApiVersion Requested version of Azure DevOps API. If not specified, $global:AzureDevOpsApi_ApiVersion (set by Set-AzureDevopsVariables) is used. .PARAMETER Uri Address of the service including query parameters. .PARAMETER Body Object to POST as a request. .PARAMETER Method The HTTP method to use. If not specified, it is decided according to whether the Body parameter is given. If not, GET is used, otherwise POST. .PARAMETER ContinuationTokenParameterName The name of the query parameter to use for the continuation token. Default is 'continuationToken'. .PARAMETER AsHashTable If specified, deserializes the JSON response to a hashtable, instead of standard [PSObject]. .PARAMETER RetryCount The maximum number of retry attempts for transient failures. Default is 3. .PARAMETER RetryDelay The base delay in seconds between retry attempts. Default is 1 second. .PARAMETER DisableRetry Disables retry logic completely. .NOTES This function is designed for Azure DevOps APIs that use continuation token pagination, such as the Test Plans API (v7.1+). .EXAMPLE $uri = "https://dev.azure.com/myorg/myproject/_apis/testplan/Plans/123/suites" $results = Invoke-ApiListPagedWithContinuationToken -Uri $uri .EXAMPLE $uri = "https://dev.azure.com/myorg/myproject/_apis/testplan/Plans/123/suites" $results = Invoke-ApiListPagedWithContinuationToken ` -Uri $uri ` -ContinuationTokenParameterName 'continuationToken' #> [CmdletBinding()] param( [AllowNull()] [PSTypeName('PSTypeNames.AzureDevOpsApi.ApiCredential')] [PSCustomObject] $ApiCredential, $ApiVersion, [Parameter(Mandatory)] $Uri, $Body, $Method, [string] $ContinuationTokenParameterName = 'continuationToken', [switch] $AsHashTable, [Parameter(ParameterSetName = 'ShowProgress')] $Activity, [ValidateRange(0, 10)] [int] $RetryCount, [ValidateRange(0.1, 300)] [double] $RetryDelay, [switch] $DisableRetry ) begin { $showProgress = $PSCmdlet.ParameterSetName -eq 'ShowProgress' # Use global configuration as defaults if not specified if (-not $PSBoundParameters.ContainsKey('RetryCount')) { $RetryCount = $global:AzureDevOpsApi_RetryConfig.RetryCount } if (-not $PSBoundParameters.ContainsKey('RetryDelay')) { $RetryDelay = $global:AzureDevOpsApi_RetryConfig.RetryDelay } if (-not $PSBoundParameters.ContainsKey('DisableRetry')) { $DisableRetry = $global:AzureDevOpsApi_RetryConfig.DisableRetry } } process { # Determine whether relative or absolute uri was given if (-not ([Uri]::new($Uri, [UriKind]::RelativeOrAbsolute)).IsAbsoluteUri) { throw "Uri must be absolute. Given: $Uri" } # If ApiVersion is not specified, use default value $ApiVersion = Use-ApiVersion -ApiVersion $ApiVersion # Add ApiVersion parameter to the Uri $genericUrl = Add-QueryParameter -Uri $Uri -Parameters @{ 'api-version' = $ApiVersion } $continuationToken = $null $pageCount = 0 $maxPages = 1000 # Safety limit to prevent infinite loops do { $pageCount++ if ($pageCount -gt $maxPages) { throw "Maximum page limit ($maxPages) exceeded. Possible infinite loop in continuation token pagination." } if ($showProgress) { Write-Progress -Activity $Activity -Status "Page $pageCount" } # Build the URI with continuation token if available if ($continuationToken) { $finalUri = Add-QueryParameter -Uri $genericUrl -Parameters @{ $ContinuationTokenParameterName = $continuationToken } } else { $finalUri = $genericUrl } # Get the data try { $httpResponse = Invoke-CustomWebRequest ` -ApiCredential $ApiCredential ` -Uri $finalUri ` -Body $Body ` -Method $Method ` -RetryCount $RetryCount ` -RetryDelay $RetryDelay ` -DisableRetry:$DisableRetry } catch { throw } $data = @($httpResponse.Content | ConvertFrom-JsonCustom -AsHashtable:$AsHashTable) # Send data down the pipeline if ($data.value) { Write-Output $data.value } # Check for continuation token in response headers if ($httpResponse.Headers -and $httpResponse.Headers['x-ms-continuationtoken']) { $continuationToken = $httpResponse.Headers['x-ms-continuationtoken'] if ($continuationToken -is [array]) { $continuationToken = $continuationToken[0] } } else { $continuationToken = $null } } while ($continuationToken) } end { if ($showProgress) { Write-Progress -Activity $Activity -Completed } } } |