Skip to content

feat: Allow Add-PatServer -Force to overwrite an existing entry#42

Merged
tablackburn merged 2 commits into
mainfrom
feature/issue-40-force-overwrite
Apr 28, 2026
Merged

feat: Allow Add-PatServer -Force to overwrite an existing entry#42
tablackburn merged 2 commits into
mainfrom
feature/issue-40-force-overwrite

Conversation

@tablackburn
Copy link
Copy Markdown
Owner

@tablackburn tablackburn commented Apr 27, 2026

Closes #40.

Summary

  • Add-PatServer -Force now replaces an existing entry with the same -Name instead of throwing, matching PowerShell convention for -Force on creation cmdlets (New-Item, Copy-Item, Register-PSRepository).
  • Replace is wholesale: fields not supplied on the new call (localUri, preferLocal, default) are not preserved. Any vault-stored token for the prior entry is removed before the new entry is written, so a fresh -Token cleanly takes over.
  • The duplicate-name error without -Force now names both recovery paths concretely: -Force for replace, Update-PatServerToken for in-place token rotation that preserves other fields.

Why replace, not merge

Microsoft's cmdlet design guidance treats -Force as "override a restriction that would normally block the command" — not as a way to change the cmdlet's fundamental semantics. Field-level updates belong in a separate Set-* / Update-* cmdlet. The module already follows this split (Update-PatServerToken, Set-PatDefaultServer); a future Set-PatServer could fill the partial-update gap if it's needed.

Test plan

  • Invoke-Pester tests/Unit/Public/Add-PatServer.tests.ps1 — 51/51 pass, including 6 new tests covering: throw without -Force, the new error message naming both -Force and Update-PatServerToken, overwrite replaces uri/token, vault token cleanup is invoked, replace is wholesale (no field merge), other servers are preserved, and -Force on a brand-new name does not invoke Remove-PatServerToken.
  • Full pwsh -File ./build.ps1 -Task Test passes.
  • Manual repro from the issue: Add-PatServer -Name 'plex' -ServerUri 'https://example:32400' -Token $newToken -Force after a prior add now succeeds.

Verified 2026-05-10 against PSGallery v0.11.2 using a throwaway name pr42-force-test: initial Add-PatServer succeeded; second add without -Force threw the new error "A server with name 'pr42-force-test' already exists. Use -Force to overwrite, or Update-PatServerToken to refresh the token in place."; second add with -Force replaced the entry (uri changed example.invalid → example2.invalid, token still in vault); Remove-PatServer cleaned up cleanly.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Add-PatServer -Force now overwrites an existing server entry instead of erroring on duplicate names.
  • Bug Fixes

    • Duplicate-name errors (when not using -Force) now include clearer guidance on recovery options.
  • Documentation

    • Help and examples updated to show -Force overwrite behavior and recommend token-rotation workflow.
  • Tests

    • Expanded unit tests covering overwrite, vault token cleanup, and non-merging semantics.

Closes #40. Matches PowerShell convention for -Force on creation cmdlets
(New-Item, Copy-Item, Register-PSRepository): on name collision, replace
the entry wholesale rather than throwing. The existing vault-stored
token is removed before the new entry is written. Without -Force the
duplicate-name error now points at both -Force and Update-PatServerToken
as concrete recovery paths.

Replace, not merge: fields not supplied on the new call (localUri,
preferLocal, default) are not preserved. Use Update-PatServerToken for
in-place token rotation that preserves other fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 27, 2026 22:53
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 27, 2026

📝 Walkthrough

Walkthrough

This change makes Add-PatServer obey PowerShell-style -Force by overwriting an existing server entry when names collide, removing any prior vault-stored token and writing the new entry; without -Force it still errors with guidance to use -Force or Update-PatServerToken.

Changes

Cohort / File(s) Summary
Changelog
CHANGELOG.md
Documents new -Force overwrite behavior, notes wholesale replacement semantics, vault token cleanup behavior, and guidance pointing to Update-PatServerToken for token-only rotation.
Add-PatServer implementation
PlexAutomationToolkit/Public/Add-PatServer.ps1
Duplicate-name check now conditional: throws only when -Force absent; when -Force is present replacement is performed within ShouldProcess — prior entry removed from in-memory config, vault token removed via Remove-PatServerToken unless incoming entry is vault-stored, and new entry appended and persisted. Help text and example updated.
Unit tests
tests/Unit/Public/Add-PatServer.tests.ps1
Adds tests for duplicate-name behavior with and without -Force, asserts error guidance, verifies overwrite semantics (non-merging, old fields removed), asserts vault cleanup calls (and skipped cases), ensures other servers preserved, and covers -WhatIf/no-op behavior. Mocks Remove-PatServerToken.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant AddPatServer
  participant ConfigStore
  participant Vault
  User->>AddPatServer: Invoke Add-PatServer -Name X -Token ... [-Force]
  AddPatServer->>ConfigStore: Check for existing server named X
  alt server not found
    AddPatServer->>ConfigStore: Append new server entry
    AddPatServer->>ConfigStore: Persist config
  else server found and -Force not set
    AddPatServer-->>User: Throw duplicate-name error with guidance
  else server found and -Force set
    AddPatServer->>AddPatServer: Enter ShouldProcess
    AddPatServer->>Vault: If old token in vault -> Remove-PatServerToken (delete)
    AddPatServer->>ConfigStore: Remove old server entry
    AddPatServer->>ConfigStore: Append new server entry (wholesale)
    AddPatServer->>ConfigStore: Persist config
    AddPatServer-->>User: Return success
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped in quick with a brave new course,
-Force in paw to change the source,
Old tokens swept from vault so deep,
New entry rests — no secrets keep,
A rabbit's cheer for safer overwrite! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: allowing Add-PatServer -Force to overwrite existing entries, which is the primary objective of the PR.
Linked Issues check ✅ Passed All primary objectives from #40 are implemented: -Force now permits overwrites, duplicate-name checks throw only without -Force, wholesale replacement semantics are applied, vault cleanup is conditional, error messages guide to both -Force and Update-PatServerToken, and operations respect -WhatIf/-Confirm within ShouldProcess.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #40 objectives; CHANGELOG, Add-PatServer implementation, and test additions comprehensively address the feature requirements without unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/issue-40-force-overwrite

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Add-PatServer -Force to follow common PowerShell -Force semantics by allowing an existing server entry with the same -Name to be replaced, and documents/tests that overwrite behavior (including vault token cleanup).

Changes:

  • Allow Add-PatServer -Force to replace an existing server entry (wholesale replacement) and remove any prior vault token before writing the new entry.
  • Improve the duplicate-name error message to explicitly suggest -Force (replace) and Update-PatServerToken (token rotation).
  • Add Pester tests for overwrite behavior and update changelog/help documentation accordingly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
PlexAutomationToolkit/Public/Add-PatServer.ps1 Implements -Force overwrite behavior, updates help text and duplicate-name error message.
tests/Unit/Public/Add-PatServer.tests.ps1 Adds unit tests covering overwrite behavior and vault token cleanup invocation.
CHANGELOG.md Documents the new -Force overwrite semantics and guidance to use Update-PatServerToken for token-only rotation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread PlexAutomationToolkit/Public/Add-PatServer.ps1 Outdated
Comment thread tests/Unit/Public/Add-PatServer.tests.ps1
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
PlexAutomationToolkit/Public/Add-PatServer.ps1 (1)

192-335: ⚠️ Potential issue | 🔴 Critical

Move all state changes behind ShouldProcess.

Remove-PatServerToken, token storage, and $configuration.servers += $newServer all happen before the ShouldProcess check, so -WhatIf or a cancelled confirmation still mutates the in-memory config and can delete the existing vault token. That makes the new overwrite path non-transactional and can break the previously working server entry even when the add is never committed.

Proposed fix
-        $configuration = Get-PatServerConfiguration -ErrorAction Stop
-        ...
-        if ($existingServer) {
-            ...
-            Remove-PatServerToken -ServerName $Name
-            $configuration.servers = @($configuration.servers | Where-Object { $_.name -ne $Name })
-        }
-        ...
-        $configuration.servers += $newServer
-
-        if ($PSCmdlet.ShouldProcess($Name, 'Add server to configuration')) {
-            Set-PatServerConfiguration -Configuration $configuration -ErrorAction Stop
-            Write-Verbose "Added server '$Name' to configuration"
-        }
+        $configuration = Get-PatServerConfiguration -ErrorAction Stop
+        if ($PSCmdlet.ShouldProcess($Name, 'Add server to configuration')) {
+            ...
+            if ($existingServer) {
+                Remove-PatServerToken -ServerName $Name
+                $configuration.servers = @($configuration.servers | Where-Object { $_.name -ne $Name })
+            }
+            ...
+            $configuration.servers += $newServer
+            Set-PatServerConfiguration -Configuration $configuration -ErrorAction Stop
+            Write-Verbose "Added server '$Name' to configuration"
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@PlexAutomationToolkit/Public/Add-PatServer.ps1` around lines 192 - 335, The
code mutates state (calls Remove-PatServerToken, Set-PatServerToken, adds token
members to $newServer, and appends to $configuration.servers) before honoring
$PSCmdlet.ShouldProcess; change to prepare all data in temporaries (e.g.,
$pendingServer, $pendingTokenStorage) and defer any destructive or persistent
actions — Remove-PatServerToken, Set-PatServerToken, adding token/tokenInVault
members to the server object, and $configuration.servers += $newServer — until
inside the ShouldProcess block where you then perform those operations and call
Set-PatServerConfiguration; keep read-only validation (Get-PatDetectedLocalUri,
Invoke-PatApi) outside but ensure all writes occur only after
$PSCmdlet.ShouldProcess($Name, ...) returns true.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@PlexAutomationToolkit/Public/Add-PatServer.ps1`:
- Around line 192-335: The code mutates state (calls Remove-PatServerToken,
Set-PatServerToken, adds token members to $newServer, and appends to
$configuration.servers) before honoring $PSCmdlet.ShouldProcess; change to
prepare all data in temporaries (e.g., $pendingServer, $pendingTokenStorage) and
defer any destructive or persistent actions — Remove-PatServerToken,
Set-PatServerToken, adding token/tokenInVault members to the server object, and
$configuration.servers += $newServer — until inside the ShouldProcess block
where you then perform those operations and call Set-PatServerConfiguration;
keep read-only validation (Get-PatDetectedLocalUri, Invoke-PatApi) outside but
ensure all writes occur only after $PSCmdlet.ShouldProcess($Name, ...) returns
true.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a86567a4-c3fc-4835-b0ab-d08307144972

📥 Commits

Reviewing files that changed from the base of the PR and between fe5832b and 0679fb1.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • PlexAutomationToolkit/Public/Add-PatServer.ps1
  • tests/Unit/Public/Add-PatServer.tests.ps1

Addresses review feedback from Copilot and CodeRabbit on PR #42. The
duplicate-name detection still fires early so -Force-less callers get
fast feedback, but the destructive overwrite operations -- removing
the existing entry from the in-memory configuration and clearing its
vault token -- now run inside the existing ShouldProcess block. Under
-WhatIf or a declined -Confirm, these no longer execute.

Remove-PatServerToken is also gated on $newServer.tokenInVault -ne
$true, so when the new -Token was stored in the vault it doesn't
clobber the just-written entry.

Tests:
- Renamed the existing overwrite vault-cleanup test to reflect that
  the cleanup is conditional on the new entry not being vault-stored.
- Added a test asserting Remove-PatServerToken is NOT called when the
  new -Token is vault-stored.
- Added a test asserting -Force overwrite under -WhatIf invokes neither
  Remove-PatServerToken nor Set-PatServerConfiguration, and leaves the
  prior in-memory config intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tablackburn
Copy link
Copy Markdown
Owner Author

Re: CodeRabbit's outside-diff finding on Add-PatServer.ps1:192-335 — fixed in cbc4d02.

The destructive ops introduced by this PR (the in-memory removal and the Remove-PatServerToken cleanup) now run inside the existing ShouldProcess block, so -WhatIf and a declined -Confirm no longer mutate state.

The proposed full restructure (pulling Set-PatServerToken and the Connect-PatAccount flow behind ShouldProcess as well) is a valid concern but is pre-existing on main and orthogonal to issue #40. Leaving that for a separate PR rather than expanding the scope of this one.

Verified by a new test that asserts no Remove-PatServerToken / Set-PatServerConfiguration calls and an unchanged in-memory config under -WhatIf overwrite.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@PlexAutomationToolkit/Public/Add-PatServer.ps1`:
- Around line 325-336: The vault writes are occurring before the ShouldProcess
check (Set-PatServerToken calls at the token handling/validation paths and
Remove-PatServerToken in the Add-PatServer flow), so move any calls that mutate
the vault (Set-PatServerToken, Remove-PatServerToken or similar) into the
ShouldProcess block where the server is actually added; keep token validation
and any in-memory token parsing/verification earlier but do not persist to the
vault until after PSCmdlet.ShouldProcess approves; add a regression test
covering the -Force -WhatIf (and -Confirm declined) case to ensure no vault
writes happen during WhatIf/preview.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8c98f926-489a-4065-82e0-8ed61e7f8b55

📥 Commits

Reviewing files that changed from the base of the PR and between 0679fb1 and cbc4d02.

📒 Files selected for processing (2)
  • PlexAutomationToolkit/Public/Add-PatServer.ps1
  • tests/Unit/Public/Add-PatServer.tests.ps1

Comment thread PlexAutomationToolkit/Public/Add-PatServer.ps1
@tablackburn tablackburn merged commit 3c1edf9 into main Apr 28, 2026
20 of 21 checks passed
@tablackburn tablackburn deleted the feature/issue-40-force-overwrite branch April 28, 2026 02:56
@tablackburn tablackburn mentioned this pull request Apr 28, 2026
2 tasks
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.

feat: Add-PatServer -Force should overwrite an existing server entry

2 participants