Batches, tenants and grants
In an app shared by several customers, each one a tenant
(Tenancy), a batch reaches only one tenant's
records: the one its saved values name. Without a tenant:, a batch
reaches every record the system is granted.
Which tenant a run reaches
Each page's read, its items and finished: act as the system, limited
to the tenant that the run's saved values of the tenant's id type name
(Background work and tenants). These
saved values count:
- a
from:argument, a valueeach:orfinished:captures, orkey:, whose own type is the tenant'sid, such asHousehold.id, or that type withnoneallowed; - a field declared with that type, as a reference to the tenant is, such
as
rent_round.society_id.
A tenant's id held inside a saved record doesn't count. The run reads the values from what it saved, so it has the same tenant each time it resumes.
Values that all name the same tenant limit the run to that tenant, as
with key: household_id beside an each: capturing household_id. A
run that saves no such value, one whose value is none, or one with
values naming different tenants, reaches no tenant's records, only those
every tenant shares.
What a batch needs from policy
A batch runs as the system, so what it reaches must let Access.system
in, as for a schedule or a subscriber
(Background work). Suppose what from:'s
query reads, or what the action each: or finished: calls reaches
(itself or through what it calls), has a grant list, a field's own list
or a line under actions: that names Access.public and not
Access.system. Then that is policy.system_grant_missing, at that
query's or action's call. Each call is checked, so one batch may be
refused at more than one.
A batch's query that only Access.public may read:
type ShelfCode = Text { min_length: 1 max_length: 40}type ItemPage = Input { page_size: PageSize { default: 25 } after: PageCursor { optional: true }}entity Item { id: Uuid code: ShelfCode seen: Bool policy: { create: [Access.authenticated] read: [Access.public] update: [Access.system] }}query items(page: ItemPage) -> Page<Item> { from: Item order: [Item.id asc] page: { size: page.page_size after: page.after }}action mark_seen(item: Item) -> Unit { var seen: Item = Item.update(item) { set: { seen: true } } assert seen.seen}action sweep_shelf(page: ItemPage) -> BatchRun { return batches.each(from: items(page), each: item -> mark_seen(item))}
error: `items` runs as the system, which only `Access.public` lets read `Item` --> main.jadpo:46:31 |46 | return batches.each(from: items(page), each: item -> mark_seen(item)) | ^^^^^^^^^^^ runs as `Access.system` reason: A batch runs as the system, which an entity grants background work by naming `Access.system`; `Item`'s `read:` names `Access.public` and not `Access.system`, and anyone at all is never how background work is let in. next: Add `Access.system` to `Item`'s `read:`, or reach `Item` from a route instead (human) rule: policy.system_grant_missing help: https://jadpo.dev/docs/errors/policy.system_grant_missingcheck failed: 1 error, 1 file
A batch's action changing what only Access.public may change:
type ShelfCode = Text { min_length: 1 max_length: 40}type ItemPage = Input { page_size: PageSize { default: 25 } after: PageCursor { optional: true }}entity Item { id: Uuid code: ShelfCode seen: Bool policy: { create: [Access.authenticated] read: [Access.public, Access.system] update: [Access.public] }}query items(page: ItemPage) -> Page<Item> { from: Item order: [Item.id asc] page: { size: page.page_size after: page.after }}action mark_seen(item: Item) -> Unit { var seen: Item = Item.update(item) { set: { seen: true } } assert seen.seen}action sweep_shelf(page: ItemPage) -> BatchRun { return batches.each(from: items(page), each: item -> mark_seen(item))}
error: `mark_seen` runs as the system, which only `Access.public` lets update `Item` --> main.jadpo:46:58 |46 | return batches.each(from: items(page), each: item -> mark_seen(item)) | ^^^^^^^^^^^^^^^ runs as `Access.system` reason: A batch runs as the system, which an entity grants background work by naming `Access.system`; `Item`'s `update:` names `Access.public` and not `Access.system`, and anyone at all is never how background work is let in. next: Add `Access.system` to `Item`'s `update:`, or reach `Item` from a route instead (human) rule: policy.system_grant_missing help: https://jadpo.dev/docs/errors/policy.system_grant_missingcheck failed: 1 error, 1 file
Markdown for agents: /docs/batches-tenants-and-grants.md.Built from Jadpo main at a707efb.