JSON Patch operations¶
RFC 6902 JSON Patch op types + a typed JsonPatchOpAdapter for round-tripping ops through DHIS2's PATCH /api/{resource}/{uid} endpoint. The discriminator on op (add, remove, replace, move, copy, test) routes to the matching BaseModel subclass.
from dhis2w_client import JsonPatchOpAdapter
op = JsonPatchOpAdapter.validate_python({"op": "replace", "path": "/name", "value": "ANC 2024"})
# -> ReplaceOp(op='replace', path='/name', value='ANC 2024')
await client.resources.programs.patch(program_uid, [op])
The adapter accepts both Python dicts and JSON strings; outputs dump back via .model_dump() / .model_dump_json(). Property-based tests in tests/test_parser_properties.py cover the round-trip and discriminator dispatch for every variant.
When to reach for this¶
- Bulk-patching one field across many resources where a full PUT round-trip is wasteful.
- Server-side conditional ops (
testfollowed byreplacein one batch). - Custom patch generators (CSV-driven sweeps, drift-detection auto-fixes).
For the higher-level batch patcher see client.metadata.patch_bulk on Metadata accessor.
json_patch
¶
Typed RFC 6902 JSON Patch operations — reusable across every DHIS2 PATCH endpoint.
DHIS2 accepts JSON Patch bodies on PATCH /api/<resource>/{id} across most
metadata types (routes, users, data elements, ...). Each op is a distinct
pydantic class tagged by the op field; the discriminated Union makes
pydantic route incoming dicts to the right variant and reject wrong-shape
bodies at construction time.
Variant shapes (per RFC 6902):
add {op, path, value} insert / overlay at path
remove {op, path} delete at path
replace {op, path, value} overwrite at path with value
test {op, path, value} assert equality; aborts on mismatch
move {op, path, from} move value from -> path
copy {op, path, from} copy value from -> path
from is a Python reserved word, so it's aliased to the from_ attribute.
JsonPatchOpAdapter.validate_python(raw) picks the right variant from a
dict; op.model_dump(by_alias=True) serialises back to the wire shape.
Attributes¶
JsonPatchOp = Annotated[AddOp | RemoveOp | ReplaceOp | TestOp | MoveOp | CopyOp, Field(discriminator='op')]
module-attribute
¶
Discriminated union over the six RFC 6902 ops. Validates {op, path, value?, from?} per op shape.
JsonPatchOpAdapter = TypeAdapter(JsonPatchOp)
module-attribute
¶
Classes¶
AddOp
¶
Bases: _JsonPatchBase
RFC 6902 add — insert value at path (or replace existing on overlap).
Source code in packages/dhis2w-client/src/dhis2w_client/v42/json_patch.py
RemoveOp
¶
ReplaceOp
¶
Bases: _JsonPatchBase
RFC 6902 replace — overwrite the value at path with value.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/json_patch.py
TestOp
¶
Bases: _JsonPatchBase
RFC 6902 test — assert the value at path equals value (aborts the patch on mismatch).
Source code in packages/dhis2w-client/src/dhis2w_client/v42/json_patch.py
MoveOp
¶
Bases: _JsonPatchBase
RFC 6902 move — move the value at from_ to path. Serialises from_ as from on the wire.
Source code in packages/dhis2w-client/src/dhis2w_client/v42/json_patch.py
CopyOp
¶
Bases: _JsonPatchBase
RFC 6902 copy — copy the value at from_ to path. Serialises from_ as from on the wire.