Add PATCH endpoints so objects can be updated without being deleted - #604
Open
JakeHuneau wants to merge 1 commit into
Open
Add PATCH endpoints so objects can be updated without being deleted#604JakeHuneau wants to merge 1 commit into
JakeHuneau wants to merge 1 commit into
Conversation
There was no update operation anywhere: "add" refuses to overwrite an
existing entry, so the only way to change a partner or a partnership was to
delete it and recreate it. That loses the definition outright if the recreate
then fails, and it is what makes an editing UI destructive by construction.
Adds "update" commands for partner and partnership that merge only what is
supplied and leave everything else alone, implemented on both the XML and the
database partnership store, and a PATCH API endpoint that exposes them:
PATCH /api/partner/<name>
PATCH /api/partnership/<name>
PATCH /api/cert/<alias>
with the attributes to change as form fields. A partnership also accepts
pollerConfig.<attr> to change one poller attribute, and sender.name or
receiver.name to point it at a different partner.
A certificate PATCH takes either a base64 certificate in "data", which
replaces a partner certificate, or a base64 PKCS12 in "data" plus the password
that opens it in "password", which replaces a certificate and its private key
together so an identity of our own can be rotated.
Replacing a key pair did not work before. importPrivateKey staged the
certificate with setCertificateEntry, which a keystore refuses on an alias
that already holds a private key because it would orphan the key, so the
import failed for exactly the alias anyone would want to rotate. The key entry
is now written in a single operation instead, which replaces whatever the alias
held, so there is no window where the alias has a certificate but no key. The
previous key pair is put back if the write fails, leaving the running server
able to keep signing with the identity it already had. The entry is stored
under the keystore password rather than the password of the file the key came
from, because that is what getPrivateKey reads it back with. This fixes the
console "cert import <alias> <file.p12> <password>" path as well.
Neither a partner nor a partnership can be renamed by an update. Other records
reference them by name, so a rename would silently orphan them; the error says
to add the replacement and delete the old one instead.
An unknown partner or partnership is returned as a command error rather than
raised, so an API caller gets the reason back instead of a server error, which
matches how the delete command already behaves.
Also fixes the neighbouring verbs, which were unusable: PUT and DELETE dropped
the first character of the item name because they pass the ID to a method that
expects it to still carry the leading path separator, and PUT and HEAD bound a
path parameter named "param" that is not in their path template, so the
resource name always arrived null.
JakeHuneau
force-pushed
the
feature/patch-endpoints
branch
from
September 8, 2026 22:36
8296a61 to
2037dd7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
There is no update operation anywhere in the codebase:
addrefuses to overwrite an existing entry, so the only way to change a partner or a partnership is to delete it and recreate it. That loses the definition outright if the recreate then fails, and it is what makes an editing UI destructive by construction.What this adds
New
updatecommands for partner and partnership that merge only what is supplied and leave everything else alone, implemented on both the XML and the database partnership store, plus a PATCH endpoint that exposes them:The attributes to change go in as form fields. A partnership also accepts
pollerConfig.<attr>to change one poller attribute, andsender.name/receiver.nameto point it at a different partner.A certificate PATCH takes either a base64 certificate in
data, which replaces a partner certificate, or a base64 PKCS12 indataplus the password that opens it inpassword, which replaces a certificate and its private key together.Replacing a key pair did not work before
importPrivateKeystaged the certificate withsetCertificateEntry, which a keystore refuses on an alias that already holds a private key because it would orphan the key. The import therefore failed for exactly the alias anyone would want to rotate, through the consolecert import <alias> <file.p12> <password>path as well as the API.The key entry is now written in a single operation, which replaces whatever the alias held, so there is no window where the alias has a certificate but no key. Three things this is careful about, since the alias in question is usually the server's own identity:
getPrivateKeyreads it back with. The old code stored an unreadable key whenever the two differed.Other behaviour
Neighbouring fixes
Two verbs in
ApiResourcewere unusable and are fixed here since a client adopting PATCH would hit them immediately:PUTandDELETEdropped the first character of the item name (DELETE /api/partner/PartnerAcalled delete onartnerA), because they hand the ID to a method that expects it to still carry the leading path separator.PUTandHEADbound@PathParam("param"), which is not in their path template, so the resource name always arrivednull.Happy to split these into a separate PR if you would rather keep them apart.
Testing
162 tests pass, 26 new.
UpdateCommandsTestcovers both stores directly: only the supplied attributes change, an attribute that was not there is added rather than duplicated, poller config merges, a partnership can be repointed, a partner change propagates to partnerships that inherit from it, and an unknown entry is refused rather than created.PatchApiTestcovers the routing over real HTTP, including that the whole item name reaches the command, that an unauthenticated PATCH is not applied, and two certificate cases: rotating an alias that holds a private key and then reading the key back through the server's own factory, and a rotation with the wrong password leaving the original certificate and private key intact.