Module adcp.decisioning.roster_store
Roster-backed :class:AccountStore factory for resolution='explicit'
publisher-curated platforms.
Use when the publisher curates accounts out-of-band (admin UI, config
file, publisher-managed DB row) and buyers pass account_id on every
request. The adopter holds a fixed allowlist of accounts; the SDK
provides the :class:AccountStore plumbing.
Pairs with the existing reference adapters. Pick by asking who creates the account?:
- Buyer self-onboards via
sync_accounts— multi-tenant adopter store (custom :class:AccountStoreimpl). Framework owns persistence; the buyer's first request to a tenant-scoped tool resolves from a prior sync. (LinkedIn, some retail-media operators.) - Upstream OAuth API owns the roster — :class:
ExplicitAccountswith a loader that calls the upstream/me/adaccounts. Returns whatever the OAuth bearer is authorized for. (Snap, Meta, TikTok.) - Publisher ops curates the roster out-of-band —
:func:
create_roster_account_store()(this module). Adopter keeps the persistence layer; the SDK provides the AccountStore. (Most SSPs, broadcasters, and retail-media networks where AE/CSM provisions the account in an internal admin tool before the buyer ever calls.)
Design Notes
- Roster IS the allowlist. Auth-based filtering happens upstream of
this layer — the framework's account-resolution gate enforces
principal-vs-account scope. The store does not consult
ctxto filterlist. - Immutable post-construction. The input dict is copied into an
internal :class:
MappingProxyTypeso external mutation of the caller's dict cannot widen the allowlist after the fact. Adopters who need a dynamic roster wrap :func:create_roster_account_store()in their own factory and rebuild on change. - Write paths fail closed per-entry. :meth:
upsertand :meth:sync_governancereturnPERMISSION_DENIEDfor every input entry rather than silently no-oping (which would lie to the buyer about their write succeeding) or operation-level raising (which would fail the whole batch on a single bad entry). Per-entry rejection matches the wire shape and lets the buyer correlate the failure to their request entry. - Resolve returns None on miss.
{brand, operator}-shaped refs and ref-less calls returnNone— publisher-curated platforms expect explicit ids. Adopters who need a synth tenant forlist_creative_formats/provide_performance_feedback/preview_creative, or natural-key resolution, wrapresolve.
Example::
from adcp.decisioning import Account, create_roster_account_store
store = create_roster_account_store(
roster={
"acct_alpha": Account(id="acct_alpha", name="Alpha", status="active"),
"acct_beta": Account(id="acct_beta", name="Beta", status="active"),
},
)
Functions
def create_roster_account_store(*, roster: Mapping[str, Account[TMeta]]) ‑> adcp.decisioning.roster_store._RosterAccountStore[~TMeta]-
Expand source code
def create_roster_account_store( *, roster: Mapping[str, Account[TMeta]], ) -> _RosterAccountStore[TMeta]: """Build an :class:`AccountStore` backed by a fixed publisher- curated roster. The returned object conforms to the :class:`AccountStore` Protocol plus the optional :class:`AccountStoreList`, :class:`AccountStoreUpsert`, and :class:`AccountStoreSyncGovernance` Protocols. ``upsert`` and ``sync_governance`` fail closed with ``PERMISSION_DENIED`` per entry — adopters who need to support write paths use a custom :class:`AccountStore` implementation instead. :param roster: Mapping from ``account_id`` → :class:`Account`. Each value's ``id`` MUST match its key; mismatch raises :class:`ValueError` at construction. The mapping is copied into an internal immutable view, so subsequent mutation of the caller's dict does not affect the store. :returns: An :class:`AccountStore` whose: * :meth:`resolve` returns the roster entry for an ``account_id``-arm ref, ``None`` otherwise. * :meth:`list` returns every roster entry. * :meth:`upsert` rejects every input entry with ``PERMISSION_DENIED``. * :meth:`sync_governance` rejects every input entry with ``PERMISSION_DENIED``. :raises ValueError: When any roster value's ``id`` does not match its dict key. """ return _RosterAccountStore(roster)Build an :class:
AccountStorebacked by a fixed publisher- curated roster.The returned object conforms to the :class:
AccountStoreProtocol plus the optional :class:AccountStoreList, :class:AccountStoreUpsert, and :class:AccountStoreSyncGovernanceProtocols.upsertandsync_governancefail closed withPERMISSION_DENIEDper entry — adopters who need to support write paths use a custom :class:AccountStoreimplementation instead.:param roster: Mapping from
account_id→ :class:Account. Each value'sidMUST match its key; mismatch raises :class:ValueErrorat construction. The mapping is copied into an internal immutable view, so subsequent mutation of the caller's dict does not affect the store.:returns: An :class:
AccountStorewhose:* :meth:<code>resolve</code> returns the roster entry for an <code>account\_id</code>-arm ref, <code>None</code> otherwise. * :meth:<code>list</code> returns every roster entry. * :meth:<code>upsert</code> rejects every input entry with <code>PERMISSION\_DENIED</code>. * :meth:<code>sync\_governance</code> rejects every input entry with <code>PERMISSION\_DENIED</code>.:raises ValueError: When any roster value's
iddoes not match its dict key.