diff --git a/Private/Invoke-PfbApiRequest.ps1 b/Private/Invoke-PfbApiRequest.ps1 index c6c7075..65e57fc 100644 --- a/Private/Invoke-PfbApiRequest.ps1 +++ b/Private/Invoke-PfbApiRequest.ps1 @@ -109,6 +109,15 @@ function Invoke-PfbApiRequest { # HTTP timeout handling — default to 30s if the connection object predates this field $restParams['TimeoutSec'] = if ($Array.HttpTimeoutMs) { [int][Math]::Ceiling($Array.HttpTimeoutMs / 1000.0) } else { 30 } + # If the caller set a page-size/limit query param (every Get-Pfb* cmdlet's -Limit maps to + # this), treat it as a hard cap on the running total across pages -- Purity//FB REST treats + # `limit` as page size only and keeps returning a continuation_token even once the caller's + # desired item count has been reached, so AutoPaginate must stop itself. + $requestedLimit = $null + if ($QueryParams -and $QueryParams.ContainsKey('limit') -and [int]$QueryParams['limit'] -gt 0) { + $requestedLimit = [int]$QueryParams['limit'] + } + $allItems = [System.Collections.Generic.List[object]]::new() $totalItemCount = $null $hasMore = $true @@ -211,7 +220,8 @@ function Invoke-PfbApiRequest { } # Handle pagination - if ($AutoPaginate -and $response.continuation_token) { + $limitReached = ($null -ne $requestedLimit -and $allItems.Count -ge $requestedLimit) + if ($AutoPaginate -and $response.continuation_token -and -not $limitReached) { # Update the URI with continuation token if (-not $QueryParams) { $QueryParams = @{} } $QueryParams['continuation_token'] = $response.continuation_token @@ -224,6 +234,12 @@ function Invoke-PfbApiRequest { } } + # The last page fetched may have overshot the requested limit (server-side page size is + # independent of it) -- trim so callers get exactly what they asked for. + if ($null -ne $requestedLimit -and $allItems.Count -gt $requestedLimit) { + $allItems = $allItems.GetRange(0, $requestedLimit) + } + # If TotalOnly was requested and we got a count but no items, return the count if ($allItems.Count -eq 0 -and $null -ne $totalItemCount) { return [PSCustomObject]@{ total_item_count = $totalItemCount } diff --git a/Tests/Invoke-PfbApiRequest.Tests.ps1 b/Tests/Invoke-PfbApiRequest.Tests.ps1 index 88d0000..99e667a 100644 --- a/Tests/Invoke-PfbApiRequest.Tests.ps1 +++ b/Tests/Invoke-PfbApiRequest.Tests.ps1 @@ -63,3 +63,70 @@ Describe 'Invoke-PfbApiRequest - HttpTimeoutMs is applied' { } } } + +Describe 'Invoke-PfbApiRequest - AutoPaginate honors -Limit' { + It 'stops paginating and trims results once the running total reaches the requested limit' { + $script:pageCallCount = 0 + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + $script:pageCallCount++ + switch ($script:pageCallCount) { + 1 { + [PSCustomObject]@{ + items = @(1..6 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + continuation_token = 'token-page-2' + } + } + 2 { + [PSCustomObject]@{ + items = @(7..12 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + continuation_token = 'token-page-3' + } + } + default { + throw "Unexpected extra page request (call #$script:pageCallCount) -- the -Limit guard should have stopped pagination after call #2" + } + } + } -ParameterFilter { $Uri -like '*file-systems*' } + + $script:result = InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{ limit = 10 } -AutoPaginate + } + + $script:result.Count | Should -Be 10 + $script:pageCallCount | Should -Be 2 + Should -Invoke -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod -Times 2 -Exactly -ParameterFilter { $Uri -like '*file-systems*' } + } + + It 'still auto-paginates the full collection when no -Limit is given' { + $script:pageCallCount2 = 0 + Mock -ModuleName PureStorageFlashBladePowerShell Invoke-RestMethod { + $script:pageCallCount2++ + if ($script:pageCallCount2 -eq 1) { + [PSCustomObject]@{ + items = @(1..6 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + continuation_token = 'token-page-2' + } + } + else { + [PSCustomObject]@{ + items = @(7..9 | ForEach-Object { [PSCustomObject]@{ name = "fs$_" } }) + } + } + } -ParameterFilter { $Uri -like '*file-systems*' } + + $script:result2 = InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{} -AutoPaginate + } + + $script:result2.Count | Should -Be 9 + $script:pageCallCount2 | Should -Be 2 + } +}