Skip to content

Centralize -Filter/-Sort/-Limit/-TotalOnly query params (#32) - #49

Merged
dmann000 merged 8 commits into
dmann000:mainfrom
juemerson-at-purestorage:feature/centralize-common-query-params
Jul 24, 2026
Merged

Centralize -Filter/-Sort/-Limit/-TotalOnly query params (#32)#49
dmann000 merged 8 commits into
dmann000:mainfrom
juemerson-at-purestorage:feature/centralize-common-query-params

Conversation

@juemerson-at-purestorage

Copy link
Copy Markdown
Collaborator

Summary

Closes #32. 30 Get-Pfb* cmdlets each hand-rolled an identical block mapping
-Filter/-Sort/-Limit/-TotalOnly (and, where applicable, accumulated
-Name/-Id pipeline input) onto the $queryParams hashtable passed to
Invoke-PfbApiRequest. This adds one shared private helper and migrates all
30 cmdlets onto it — pure internal refactor, no public parameter/behavior
change intended beyond one deliberate fix noted below.

New helper: Private/Add-PfbCommonQueryParams.ps1, with 24 Pester tests
covering each scalar independently, -Names/-Ids joining, and in-place
hashtable mutation.

Migrated (30 cmdlets, in 7 batches): Bucket (3) · FileSystem (9) ·
FileSystemSnapshot/Replication (3) · ObjectStore (15) · Realm · Server.

Intentional behavior change: the helper checks .ContainsKey() on the
four scalars instead of truthiness, so an explicit -Limit 0 is now sent to
the API where the old per-cmdlet if ($Limit -gt 0) guard silently dropped
it. (An empty-string -Filter '' is unaffected in practice — ConvertTo-PfbQueryString
already strips null/empty values before they reach the wire.)

Wire-key handling: several cmdlets map their Name/Id-equivalent params to
non-generic wire keys (file_system_names/ids, policy_names/ids,
role_names/ids, member_names/ids) instead of the helper's generic
names/ids. Those are left as the cmdlet's own untouched lines rather than
being routed through the helper, so the actual wire format sent to the array
is unchanged. One cmdlet (Get-PfbObjectStoreAccessPolicyRule) mixes both —
handled by passing only the generic-keyed param through the helper.

Out of scope (follow-up candidate): ~55 other Get-Pfb* cmdlets without
a -TotalOnly param still hand-roll filter/sort/limit and weren't
touched here, per the issue's own scoping.

Test plan

  • New unit tests for Add-PfbCommonQueryParams (24/24 passing)
  • Full existing suite green throughout (404 passed / 0 failed / 6 skipped, unchanged)
  • Existing dedicated tests that intersect this refactor pass unmodified:
    Get-PfbFileSystemSession.Tests.ps1, ObjectStoreAccessPolicyAssociations.Tests.ps1
  • Live-verified against a real FlashBlade array (FB-A) after every batch —
    confirmed correct query strings for -Limit/-Sort/-Filter/-TotalOnly
    and every non-generic/mixed wire-key case
  • Completeness sweep: all 30 originally-identified cmdlets confirmed to
    call the helper, zero leftover hand-rolled total_only assignments

Notes

Live-testing incidentally reproduced the -Limit/AutoPaginate pagination
bug already tracked and fixed by #30 (PR #48, still open) — no file overlap
with that PR, confirmed no conflict either way this merges.

juemerson-at-purestorage and others added 8 commits July 23, 2026 14:19
)

Adds Private/Add-PfbCommonQueryParams.ps1 with the shared helper that
centralizes -Filter/-Sort/-Limit/-TotalOnly/-Names/-Ids parameter handling
across all 30 Get-Pfb* cmdlets. Includes comprehensive Pester tests covering
all scalar parameters individually, ContainsKey semantics (including -Limit 0
edge case), Names/Ids array joining, and in-place hashtable mutation.

All 24 tests pass.
Replace the hand-rolled filter/sort/limit/total_only/names/ids
mapping in Get-PfbBucket, Get-PfbBucketPerformance, and
Get-PfbBucketS3Performance with the shared Add-PfbCommonQueryParams
helper introduced in Task 1. Cmdlet-specific query params
(-Destroyed, -StartTime/-EndTime/-Resolution) are left untouched.
Replace the hand-rolled filter/sort/limit/total_only/names/ids block in
Get-PfbFileLock, Get-PfbFileLockClient, Get-PfbFileSystem, and
Get-PfbFileSystemExport with the shared Add-PfbCommonQueryParams helper,
matching the pattern already used by the Bucket cmdlets.

Part of dmann000#32.
Replace the hand-rolled filter/sort/limit/total_only(/names/ids) block in
Get-PfbFileSystemGroupPerformance, Get-PfbFileSystemSession,
Get-PfbFileSystemStorageClass, Get-PfbFileSystemUserPerformance, and
Get-PfbOpenFile with the shared Add-PfbCommonQueryParams helper.

Get-PfbFileSystemGroupPerformance and Get-PfbFileSystemUserPerformance keep
their file_system_names/file_system_ids query keys as cmdlet-specific lines
(not routed through -Names/-Ids) since that differs from the generic
names/ids convention the helper hardcodes.

Part of dmann000#32.
…yParams (dmann000#32)

Replaces the hand-rolled filter/sort/limit/total_only/names/ids query-param
blocks in Get-PfbFileSystemSnapshot, Get-PfbFileSystemSnapshotTransfer, and
Get-PfbFileSystemReplicaLinkTransfer with calls to the shared helper. All
three use the generic names/ids wire keys, so no cmdlet-specific naming
divergence applies here.
…n000#32)

Replaces the hand-rolled filter/sort/limit/total_only query-param blocks in
Get-PfbObjectStoreAccessPolicy(Action|Role|Rule|User) with calls to the
shared Add-PfbCommonQueryParams helper. Get-PfbObjectStoreAccessPolicyRole
and -User use non-generic policy_names/policy_ids/member_names/member_ids
wire keys, so the helper is called without -Names/-Ids there; Rule mixes a
generic 'names' key (from -Name) with non-generic policy_names/policy_ids
(from -PolicyName/-PolicyId), so only -Names is passed through.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n000#32)

Get-PfbObjectStoreAccount, Get-PfbObjectStoreAccountExport,
Get-PfbObjectStoreRemoteCredential, and Get-PfbObjectStoreRole use
generic names/ids wire keys, so their end{} blocks now route
-Names/-Ids through the shared helper.

Get-PfbObjectStoreRoleAccessPolicy maps its Role/Member name and id
params to non-generic role_names/role_ids/member_names/member_ids
wire keys, so the helper is called without -Names/-Ids and those four
lines are left as the cmdlet's own untouched query-param assignments.
…QueryParams (dmann000#32)

Final migration batch: Get-PfbObjectStoreTrustPolicy, Get-PfbObjectStoreTrustPolicyRule,
Get-PfbObjectStoreUserAccessPolicy, Get-PfbRealm, and Get-PfbServer now route their
Filter/Sort/Limit/TotalOnly (and generic Names/Ids where applicable) query-param
assembly through the shared helper, matching the prior 25-cmdlet migration.

Completeness sweep confirms all 30 originally-identified Get-Pfb* cmdlets now call
Add-PfbCommonQueryParams, with no hand-rolled total_only assignments remaining
outside Private/Add-PfbCommonQueryParams.ps1.
@dmann000
dmann000 merged commit 06e859f into dmann000:main Jul 24, 2026
4 checks passed
juemerson-at-purestorage added a commit that referenced this pull request Jul 25, 2026
…and parser gaps (#53)

* fix(tools): teach the parameter inventory two wire-name idioms it could not see

tools/lib/PfbCmdletParamTools.ps1 resolved a parameter's REST wire name only from
a literal `$body['k'] = $Param` / `$queryParams['k'] = $Param` index assignment in
the cmdlet's own body. Two very common idioms were therefore invisible, and every
parameter they cover was classified AttributesOnly/TypedUnresolved. Downstream,
Get-PfbParameterCoverageGaps requires EVERY parameter on EVERY cmdlet calling an
endpoint to be Surface='Typed', so one unresolved parameter pushed the whole
endpoint into the notVerified bucket and out of gap analysis entirely.

1. The shared Private/Add-PfbCommonQueryParams.ps1 helper (issue #32/PR #49,
   extended to ~196 cmdlets by issue #33/PR #51) assigns -Filter/-Sort/-Limit/
   -TotalOnly/-Names/-Ids on the caller's behalf, so migrated cmdlets contain no
   literal key at all. This made a pure mechanical refactor look like a 39%
   improvement: on PR #51's tree the headline missing-field count FELL 932 -> 571
   while notVerified endpoints ROSE 194 -> 349. Added Get-PfbCommonQueryParamMap
   (a documented hardcoded mirror of the helper) plus
   Get-PfbCommonQueryParamHelperWireName. -Filter/-Sort/-Limit/-TotalOnly are
   matched by PARAMETER NAME (the helper reads them from the caller's
   $PSBoundParameters, and the call must actually forward it); -Names/-Ids by the
   variable passed to that argument, which the existing accumulator retry already
   traces from the parameter. Runs LAST so the non-generic keys those cmdlets
   deliberately kept as explicit lines (policy_names, file_system_names, ...) still win.

2. The hashtable-literal initializer `$queryParams = @{ 'names' = $Name }` -- the
   dominant shape across New-Pfb*/Remove-Pfb*/Update-Pfb* -- was only ever matched
   in its IndexExpressionAst form. Added
   Get-PfbHashtableLiteralWireNameForParameter, top-level pairs only: in
   `$body = @{ group = @{ name = $GroupName } }` the wire field is `group`, so
   crediting the inner `name` would mis-name the field and collide with every other
   sub-object's `name`.

Also extracted the RHS shape test (Test-PfbWireValueIsParameter) and the parser's
StatementAst unwrapping (Resolve-PfbSingleExpression) so both paths share one
deliberately shape-exact matcher rather than duplicating it -- a pipeline transform
is still refused rather than guessed at.

Measured, generating the report from each tools/lib against each cmdlet tree
(reporting only; no Public/, Private/, Data/ or Reports/ change):

                     | gaps | notVerified | missing fields | uncovered
  baseline    main   |  254 |         194 |            932 |       117
  baseline    PR #51 |  132 |         349 |            571 |       117
  fix 1 only  main   |  282 |         164 |           1019 |       117
  fix 1 only  PR #51 |  286 |         157 |           1029 |       117
  both fixes  main   |  343 |         101 |           1370 |       117
  both fixes  PR #51 |  347 |          94 |           1380 |       117

The 155-endpoint main-vs-PR#51 spread closes to 7, zero endpoints newly become
notVerified in any transition, and no already-resolved wire name changes. The
residual 7 are cmdlets PR #51 gave query parameters to that had none on main
(Get-PfbDns, Get-PfbAdminSetting, ...), which sidesteps a separate pre-existing
bug in tools/lib/PfbApiDriftTools.ps1: `@($inventoryByCmdlet[$cmdletName])` yields
@($null) for a cmdlet with zero inventory rows, wrongly marking its endpoint
notVerified. That accounts for 37 of the remaining 101 and is left for its own change.

Tests: 24 new cases in Tests/PfbCmdletParamTools.Tests.ps1 over synthetic
Public/-shaped fixtures, including a mapping-table-vs-helper sync test that derives
the contract from Private/Add-PfbCommonQueryParams.ps1's own AST so the hardcoded
mirror cannot silently drift (mutation-verified: it fails when the helper gains a key).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(tools): stop discarding parameterless cmdlets and nested reference objects

Two more instances of the same failure mode PR #53 opened for -- the drift
report going quieter, which reads as better -- found while scoping the
"make the drift report actionable" work.

1. Null-vs-empty conflation in Get-PfbParameterCoverageGaps. The per-cmdlet
   inventory lookup is a Group-Object -AsHashTable, which returns $null for an
   absent key, and @($null) is a ONE-element array whose element is $null. The
   surface loop therefore ran once with $row = $null, read $null.Surface -ne
   'Typed' as true, and discarded the endpoint into notVerified. A cmdlet
   contributes zero inventory rows when every parameter it declares is
   -Array/-Attributes (both inventory-exempt) -- the exact shape of the
   -Attributes-only write cmdlets -- and nothing contradicting full mapping
   means vacuously fully mapped, not unmappable. 36 endpoints leave
   notVerified, including PATCH /arrays, PATCH /password-policies and
   PATCH /support with 18/19/12 previously invisible missing fields.

2. Nested single-key reference objects, `$body['account'] = @{ name = $Account }`,
   are now credited to their OUTER key. The capability map records top-level
   body properties only, so `account.name` is not a field it can track and
   `account` is the field such a parameter actually covers. Two parameters
   resolving to the same outer key (by name, by id) is correct, not a collision
   to suppress. Resolves 13 (cmdlet, parameter) pairs across 9 cmdlets, and via
   the notVerified gate 6 more endpoints. New resolver runs strictly after both
   direct-assignment passes, so it can only turn an unresolved parameter Typed,
   never rename an already-resolved wire name (measured: 0 wire-name changes,
   0 Typed -> non-Typed regressions).

Combined, notVerified 101 -> 59 with 0 newly-notVerified, reported missing
fields 1370 -> 1538 with 0 fields removed, and the residual 7-endpoint
notVerified spread against PR #51's tip closes to 0.

Measured, not asserted; report regenerated to scratch paths only -- committed
Data/ and Reports/ artifacts unchanged. Live-array testing not applicable:
tools/ and Tests/ only, no runtime surface, no Public//Private/ change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Centralize -Limit/-Sort/-Filter/-TotalOnly query-param handling in Invoke-PfbApiRequest

2 participants