You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PUT [type]?[criteria] and DELETE [type]?[criteria] entries are resolved in batch bundles (#511), but in a transaction any non-GET entry whose URL carries a query string still declines the whole bundle with 400 not-supported before anything executes (crates/rest/src/handlers/batch.rs, transaction guard). The spec's own transaction example (crates/fhir/tests/data/json/R4/bundle-transaction.json, entries 3 and 6) uses both forms.
Why it is not a small change
The backends' transaction executors (process_bundle_entry_tx in sqlite/postgres, process_bundle_entry_transaction in mongodb) address PUT and DELETE through parse_url, which is query-blind and takes the last two path segments; Patient?identifier=x yields a single segment and fails.
ConditionalStorage::conditional_update/delete write through the backend, not the open transaction. Calling them from inside process_transaction would commit outside the bundle's atomic scope.
batch/transaction: conditional interactions are refused rather than resolved — wire bundle entries to ConditionalStorage #511 added the in-transaction search primitive (SqliteBackend::search_with_connection, PostgresBackend::search_with_client, used by find_matching_resources_in_tx), so the match side is available. What is missing is the resolve-then-write inside the transaction for PUT (update the match or create) and DELETE (delete the match), plus MongoDB's equivalent on its session-scoped matcher.
R4 §3.1.0.11.2: identities resolved from conditional update/delete that overlap with other entries in the bundle SHALL fail the transaction. That needs a pre-resolution pass over the ordered entries before any write, in the shape fix(transaction): resolve conditional references before execution #467 chose for conditional references.
Proposed shape
Split each backend's PUT/DELETE arm on conditional_criteria(url); on criteria, resolve via the in-transaction matcher, then update/create or delete the match through the transaction, answering with the same status mapping the batch arm uses (200/201/412 for PUT, 204/204/412 for DELETE). Reuse multiple_matches_entry from core::preconditions.
Add the overlap pre-pass in process_transaction: resolve every conditional entry's target first, fail the bundle if two entries resolve to the same Type/id or a conditional target collides with an explicit instance entry.
Percent-decode criteria before they reach parse_simple_search_params; the batch arm's normalize_criteria is the precedent.
Tests to add
batch_conformance: transaction PUT Patient?identifier=… updating a seeded match, creating on no match, 412 on several; same for DELETE; the spec fixture as a whole once $op entries (separate issue) are handled.
persistence transactions suite: rollback of an earlier create when a later conditional entry is ambiguous; overlap pre-pass rejecting two entries resolving to one resource.
Split out of #511, where batch resolution and in-transaction ifNoneExist landed.
Summary
PUT [type]?[criteria]andDELETE [type]?[criteria]entries are resolved in batch bundles (#511), but in a transaction any non-GETentry whose URL carries a query string still declines the whole bundle with400 not-supportedbefore anything executes (crates/rest/src/handlers/batch.rs, transaction guard). The spec's own transaction example (crates/fhir/tests/data/json/R4/bundle-transaction.json, entries 3 and 6) uses both forms.Why it is not a small change
process_bundle_entry_txin sqlite/postgres,process_bundle_entry_transactionin mongodb) address PUT and DELETE throughparse_url, which is query-blind and takes the last two path segments;Patient?identifier=xyields a single segment and fails.ConditionalStorage::conditional_update/deletewrite through the backend, not the open transaction. Calling them from insideprocess_transactionwould commit outside the bundle's atomic scope.SqliteBackend::search_with_connection,PostgresBackend::search_with_client, used byfind_matching_resources_in_tx), so the match side is available. What is missing is the resolve-then-write inside the transaction for PUT (update the match or create) and DELETE (delete the match), plus MongoDB's equivalent on its session-scoped matcher.Proposed shape
conditional_criteria(url); on criteria, resolve via the in-transaction matcher, then update/create or delete the match through the transaction, answering with the same status mapping the batch arm uses (200/201/412 for PUT, 204/204/412 for DELETE). Reusemultiple_matches_entryfromcore::preconditions.process_transaction: resolve every conditional entry's target first, fail the bundle if two entries resolve to the sameType/idor a conditional target collides with an explicit instance entry.ifNoneExist).parse_simple_search_params; the batch arm'snormalize_criteriais the precedent.Tests to add
PUT Patient?identifier=…updating a seeded match, creating on no match, 412 on several; same for DELETE; the spec fixture as a whole once$opentries (separate issue) are handled.Split out of #511, where batch resolution and in-transaction
ifNoneExistlanded.