# Subscribers

A subscriber reacts to an event: each time the event is committed, it
runs an action. It names one event in `on:` and the action to run in
`run:`. Inside `run:`, `event` is the event delivered. This one keeps a note
of each `TodoCompleted` from [Declaring an event](/docs/declaring-an-event.md):

`domains/planning/subscribers/update_statistics.jadpo`:

```jadpo
use planning/actions { note_todo }
use planning/events { TodoCompleted }

/// Counts each completed todo.
subscriber update_statistics {
    on: TodoCompleted
    run: note_todo(event)
}
```

`domains/planning/actions/note_todo.jadpo`:

```jadpo
use planning/entities { TodoNotFound, TodoNote }
use planning/events { TodoCompleted }

/// Keeps a note of a completed todo.
action note_todo(completed: TodoCompleted) fails TodoNotFound -> TodoNote {
    return attempt TodoNote.create {
        set: {
            todo_id: completed.todo_id
            completed_at: completed.completed_at
        }
    }
}
```

`domains/planning/entities/todo_note.jadpo`:

```jadpo
use planning/entities { Todo }

/// A note that a todo was completed, kept for statistics.
entity TodoNote {
    id: Uuid
    todo_id: Todo.id {
        on_delete: OnDelete.cascade
    }
    completed_at: Instant

    policy: {
        create: [Access.system]
        read: [Access.system]
    }
}
```

A subscriber has no person behind it, so `TodoNote` and the `Todo` it
refers to let `Access.system` in.

## What a subscriber holds

A subscriber holds three things, and nothing else
(`subscriber.item_invalid`):

- `on:`, the event type. Another domain's event must be `public` and
  imported.
- `run:`, a call of an action in the subscriber's own domain
  (`subscriber.run_invalid`). Its arguments are `event` or fields read
  through it, such as `event.todo_id` (`subscriber.argument_invalid`),
  and they are type-checked against the action's parameters.
- `retry:`, optionally ([Retries and giving up](/docs/retries-and-giving-up.md)).

## One event each

A subscriber listens for one event. To react to two events, write two
subscribers.

## Each subscriber on its own

Every subscriber of an event gets its own delivery. One failing doesn't
affect the others. Nothing that happens in a subscriber reaches the
action that emitted the event, which has already committed and returned.
A subscriber's action may emit further events.

## Runs as the system

A subscriber runs as the system (`Access.system`,
[Policy and background work](/docs/policy-and-background-work.md)), not as the user who
caused the event. If it needs to know who, put a field such as
`by: User.id` in the event. What its action could reach whose list doesn't
name `Access.system` is `policy.system_grant_missing`, so a write it could
never make fails at `jadpo check`, not as a failed delivery.

The system may write a `FieldAccess.server_only` field, as any other, where
the record's own list names `Access.system`: the field keeps out requests,
and the system is none.

## What a subscriber stores from its event

Jadpo checks that a subscriber doesn't store a value from its event
where people who may not read the value could see it.

What an event carries is everything any `emit` of it put there, apart
from what a request sent. Along a chain of events, it also includes what
a subscriber's action emits from the event it was given. That is
because each action's `emit`s, and those of the actions it calls, count
as part of what it passes to its callers.

What the subscriber's action stores from what it is passed is checked
where the subscriber passes it, as a call is
([Policy](/docs/policy.md)), and refused with
`policy.output_field_unproved` at the argument. What decided an `emit`,
and any `reject` in its action, go with every field of the event.

## A value from an event has left its record

The record a value came from may change or move between the `emit` and
the delivery. So a value an event carries is checked as though it came
from any record of its entity. Of the field's readers, only "anyone
signed in" and roles held across the whole app carry over.

The one link kept is the tenant. When the event's tenant field came from
the same record as a value, that record was the tenant's, and the
subscriber is limited to that tenant. So the value may be stored
wherever that tenant's roles may read it.

A subscriber that needs more passes the event's ids and reads the
current state, as [Declaring an event](/docs/declaring-an-event.md) advises.
