From b5f5e56fc81627e93f274135695135dba919302a Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Thu, 23 Jul 2026 11:25:46 -0700 Subject: [PATCH 1/4] test: add failing regression test for AutoPaginate ignoring -Limit (#30) --- Tests/Invoke-PfbApiRequest.Tests.ps1 | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/Tests/Invoke-PfbApiRequest.Tests.ps1 b/Tests/Invoke-PfbApiRequest.Tests.ps1 index 88d0000..c0bd19a 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*' } + + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + $script:result = 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*' } + + InModuleScope PureStorageFlashBladePowerShell { + $array = [PSCustomObject]@{ + Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' + ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false + } + $script:result2 = Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{} -AutoPaginate + } + + $script:result2.Count | Should -Be 9 + $script:pageCallCount2 | Should -Be 2 + } +} From 3652a46424cb9aea72fb26c3d64dfbc244d3b96f Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Thu, 23 Jul 2026 11:32:37 -0700 Subject: [PATCH 2/4] fix: capture InModuleScope return value instead of assigning $script: inside it (#30 test fix) --- Tests/Invoke-PfbApiRequest.Tests.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Invoke-PfbApiRequest.Tests.ps1 b/Tests/Invoke-PfbApiRequest.Tests.ps1 index c0bd19a..99e667a 100644 --- a/Tests/Invoke-PfbApiRequest.Tests.ps1 +++ b/Tests/Invoke-PfbApiRequest.Tests.ps1 @@ -88,12 +88,12 @@ Describe 'Invoke-PfbApiRequest - AutoPaginate honors -Limit' { } } -ParameterFilter { $Uri -like '*file-systems*' } - InModuleScope PureStorageFlashBladePowerShell { + $script:result = InModuleScope PureStorageFlashBladePowerShell { $array = [PSCustomObject]@{ Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false } - $script:result = Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{ limit = 10 } -AutoPaginate + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{ limit = 10 } -AutoPaginate } $script:result.Count | Should -Be 10 @@ -118,12 +118,12 @@ Describe 'Invoke-PfbApiRequest - AutoPaginate honors -Limit' { } } -ParameterFilter { $Uri -like '*file-systems*' } - InModuleScope PureStorageFlashBladePowerShell { + $script:result2 = InModuleScope PureStorageFlashBladePowerShell { $array = [PSCustomObject]@{ Endpoint = 'fb.test'; ApiVersion = '2.26'; AuthToken = 'tok' ApiToken = $null; AuthMethod = 'ApiToken'; SkipCertificateCheck = $false } - $script:result2 = Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{} -AutoPaginate + Invoke-PfbApiRequest -Array $array -Method GET -Endpoint 'file-systems' -QueryParams @{} -AutoPaginate } $script:result2.Count | Should -Be 9 From 3c12aee03e4ec459934028f0c516339a2b1d7bcb Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Thu, 23 Jul 2026 11:43:00 -0700 Subject: [PATCH 3/4] fix: stop AutoPaginate once the running item total reaches -Limit (#30) --- Private/Invoke-PfbApiRequest.ps1 | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/Private/Invoke-PfbApiRequest.ps1 b/Private/Invoke-PfbApiRequest.ps1 index c6c7075..464b855 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 $QueryParams['limit']) { + $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 } From 8dbbf15cee36212c3ac130cbcedf0cd92d77d968 Mon Sep 17 00:00:00 2001 From: Justin Emerson Date: Thu, 23 Jul 2026 12:00:27 -0700 Subject: [PATCH 4/4] fix: reject non-positive -Limit in the pagination guard (#30 follow-up) Final-review finding: the truthy check on $QueryParams['limit'] admitted a negative limit, which would make the post-loop GetRange() throw instead of behaving as "no limit". Unreachable via any current public cmdlet (all validate limit > 0 before setting the query param), but tightening the private function's own guard removes the latent crash and the separate limit=0 gap in one edit. --- Private/Invoke-PfbApiRequest.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Private/Invoke-PfbApiRequest.ps1 b/Private/Invoke-PfbApiRequest.ps1 index 464b855..65e57fc 100644 --- a/Private/Invoke-PfbApiRequest.ps1 +++ b/Private/Invoke-PfbApiRequest.ps1 @@ -114,7 +114,7 @@ function Invoke-PfbApiRequest { # `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 $QueryParams['limit']) { + if ($QueryParams -and $QueryParams.ContainsKey('limit') -and [int]$QueryParams['limit'] -gt 0) { $requestedLimit = [int]$QueryParams['limit'] }