Sharing helpers¶
Typed helpers over /api/sharing. Use them to read and replace the sharing block of any DHIS2 object without hand-writing JSON Patch.
Access strings¶
DHIS2 packs four capabilities into an 8-char string:
| Positions | Meaning |
|---|---|
| 0–1 | metadata read / write — rw, r-, -- |
| 2–3 | data read / write (only meaningful on dataValue-carrying objects) |
| 4–7 | reserved; always ---- |
from dhis2w_client import access_string, ACCESS_READ_METADATA, ACCESS_READ_WRITE_DATA
access_string() # '--------'
access_string(metadata="rw") # 'rw------'
access_string(metadata="r-", data="r-") # 'r-r-----'
ACCESS_READ_METADATA # 'r-------'
ACCESS_READ_WRITE_DATA # 'rwrw----'
The four constants cover >95% of callsites; access_string() handles the rest without concatenation at the use site.
Get / apply¶
from dhis2w_client import Dhis2Client, SharingBuilder, apply_sharing, get_sharing
async with Dhis2Client(url, auth) as client:
current = await get_sharing(client, "dataSet", ds_uid)
# current is a `SharingObject` (publicAccess, user, userAccesses[], userGroupAccesses[]).
# DHIS2 defines no `externalAccess` on `SharingObject` on any supported major:
# the field is absent from the OpenAPI document, and a write carrying it answers
# 200 "Access control set" while discarding the value (BUGS.md #38).
sharing = (
SharingBuilder(owner_user_id=admin_uid)
.grant_user(admin_uid, "rwrw----")
.grant_user_group(group_uid, "r-------")
)
envelope = await apply_sharing(client, "dataSet", ds_uid, sharing)
assert envelope.status == "OK"
apply_sharing accepts either a SharingBuilder or a raw SharingObject (use the builder for ergonomics, drop down to the object when you already have one from get_sharing).
Resource types¶
resource_type is DHIS2's singular metadata name as it appears in filter strings and in the sharing API's ?type= param. Common values:
dataSet,dataElement,categoryCombo,categoryOptionprogram,programStage,trackedEntityTypeuser,userGroup,userRoledashboard,visualization,map,report,eventChart,eventReport
DHIS2 returns 409 Conflict with Type <name> is not supported. for anything the sharing endpoint will not serve. Status and message are identical whether the type does not exist at all or exists but has no sharing, so the response alone does not tell the two apart. Check /api/schemas/{type} to find out which you hit: a 404 means the name is wrong (the sharing endpoint uses the same singular names as the schema), and a 200 carrying "shareable": false means the type is real but has no sharing block — organisationUnit is the one people try most often.
Why this exists¶
Before this helper, callers typically reached for JSON Patch against /api/<resource>/<uid>:
# Old pattern — works but verbose, error-prone, untyped.
await client.patch_raw(
f"/api/dataSets/{uid}",
[{"op": "add", "path": "/sharing/users", "value": {admin_uid: {"id": admin_uid, "access": "rwrw----"}}}],
)
The typed helper replaces that with one line, and get_sharing + builder composition makes additive edits (preserve everything, append one grant) straightforward. See examples/client/bootstrap_zero_to_data.py step 5 for the before/after.
sharing
¶
Typed helpers for DHIS2's sharing API.
DHIS2 models access to every persistable object as a sharing block: a
public-access string, an owning user, and two lists of per-user / per-user-group
grants. The wire shape has two flavours:
SharingObject— what/api/sharing?type=<t>&id=<uid>returns insideSharingInfo.object. CarriespublicAccess,userAccesses,userGroupAccesses.Sharing— what metadata resources carry on theirsharingfield. Same information butuserAccesses[]becomes a keyed mapusers: {uid: {...}}(same for groups), matching how DHIS2 stores it internally.
This module ships three utilities:
access_string(metadata="rw", data="rw")— compose the 8-char access string DHIS2 uses ("rwrw----","r-------", etc.). Constants for the common forms.get_sharing(client, resource_type, uid)— fetch the current block viaGET /api/sharing.apply_sharing(client, resource_type, uid, sharing)— replace it viaPOST /api/sharing. Returns aWebMessageResponse.- A
SharingBuilderconvenience for building upSharingObjectwithout typing the map-vs-list details by hand.
Classes¶
Sharing
¶
Bases: BaseModel
OpenAPI schema Sharing.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/oas/sharing.py
SharingObject
¶
Bases: BaseModel
OpenAPI schema SharingObject.
Source code in packages/dhis2w-client/src/dhis2w_client/generated/v43/oas/sharing_object.py
SharingBuilder
¶
Bases: BaseModel
Fluent-ish builder for SharingObject so callers don't hand-assemble maps.
Callers that want to attach fresh sharing to an object work in terms of
"grant user X read+write", not "build a SharingUserAccess and append it
to the list". The builder hides that boilerplate while producing the exact
wire shape POST /api/sharing wants.
DHIS2 defines no externalAccess on SharingObject: the field is absent
from the OpenAPI document on every supported major, and a write that
carries it answers 200 "Access control set" while discarding the value.
The builder exposes no external_access knob and the materialised wire
shape names no externalAccess (BUGS.md #38).
Source code in packages/dhis2w-client/src/dhis2w_client/v43/sharing.py
Methods:¶
grant_user(user_id, access)
¶
Grant access to one user (overwrites any existing grant).
Source code in packages/dhis2w-client/src/dhis2w_client/v43/sharing.py
grant_user_group(group_id, access)
¶
Grant access to one user group (overwrites any existing grant).
Source code in packages/dhis2w-client/src/dhis2w_client/v43/sharing.py
to_sharing_object()
¶
Materialise the builder into the SharingObject wire shape.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/sharing.py
Functions:¶
access_string(*, metadata='--', data='--')
¶
Compose an 8-char DHIS2 access string from metadata + data r/w pairs.
Examples:
>>> access_string(metadata="rw") # metadata-only read/write
'rw------'
>>> access_string(metadata="r-", data="r-") # read-everything
'r-r-----'
>>> access_string(metadata="rw", data="rw") # full access
'rwrw----'
Source code in packages/dhis2w-client/src/dhis2w_client/v43/sharing.py
get_sharing(client, resource_type, uid)
async
¶
Fetch GET /api/sharing?type=<resource_type>&id=<uid> → SharingObject.
resource_type is the DHIS2 singular metadata-resource name as it appears
in filter syntax and in the sharing API's ?type= param — e.g.
"dataSet", "dataElement", "program", "user". DHIS2 returns a
{meta, object} envelope; callers almost always want just .object.
Source code in packages/dhis2w-client/src/dhis2w_client/v43/sharing.py
apply_sharing(client, resource_type, uid, sharing)
async
¶
Replace the sharing block via POST /api/sharing?type=<t>&id=<uid>.
DHIS2 ignores unknown fields and preserves the owner user when the payload
omits it. Accepts either a SharingObject (raw wire shape) or a
SharingBuilder (convenience form).