ores.inbox.approval_operations

Table of Contents

The operations a person takes on an approval request. They are not entity verbs: raising a request fills in who asked, when it expires and where it starts; deciding one writes a decision and moves the request to the state the decisions reach, in one transaction. The entity protocols of ores.inbox.approval_request and ores.inbox.approval_decision stay the administrator's surface; these are the member's.

The handler is hand-written beside this model, because each operation applies the lifecycle rules of Approval requests.

1. Physical space

The shell commands are switched on. These operations act as the signed-in person, so a shell session signed in as that person asks, decides and reads the queue as they would, and a script of those commands proves the lifecycle end to end.

2. Includes

#include "ores.inbox.api/domain/approval_request.hpp"
#include "ores.utility/domain/protocol.hpp"
#include <chrono>
#include <string>
#include <vector>

3. Messages

3.1. raise_approval_request_request

/**
 * @brief Asks for something that a person who may decide it must approve.
 *
 * The request is raised for the signed-in person, waiting, and expiring when
 * its kind says. The component that owns the kind writes the detail that names
 * what is asked for, keyed by the request's id.
 */

3.1.1. kind_code

/**
 * @brief The kind of request, such as iam.role_grant.
 */

3.1.2. reason

/**
 * @brief Why the person asks, in their words.
 */

3.1.3. part_codes

/**
 * @brief The parts whose approval the request needs.
 *
 * Empty for a kind that names one decider permission and a count. When parts
 * are named, the request is approved once every part has approved, in the
 * answer order the parts give.
 */

3.2. raise_approval_request_response

3.2.1. result

3.2.2. request

/**
 * @brief The request as raised, when the outcome is ok.
 */

3.3. withdraw_approval_request_request

/**
 * @brief Takes back a request the signed-in person raised.
 *
 * Only an open request can be withdrawn, and only by the person who asked.
 */

3.3.1. request_id

3.3.2. version

/**
 * @brief The version of the request the person saw.
 */

3.3.3. comment

3.4. withdraw_approval_request_response

3.4.1. result

3.4.2. request

3.5. decide_approval_request_request

/**
 * @brief Approves, refuses, holds or resumes a request.
 *
 * The decider needs the permission the request's kind names, and is not the
 * person who asked. The request moves to the state the decisions reach: an
 * approval closes it only once the kind's approvals_required approvals stand.
 */

3.5.1. request_id

3.5.2. version

/**
 * @brief The version of the request the decider saw, so a second decision
 * on a request someone else has just decided is refused.
 */

3.5.3. decision_code

/**
 * @brief approve, refuse, hold or resume.
 */

3.5.4. comment

3.5.5. part_code

/**
 * @brief The part the decider answers for.
 *
 * Required to approve or refuse a request that names parts. The decider needs
 * the permission that part names.
 */

3.5.6. hold_reason_code

/**
 * @brief Why the request is parked, when the decision is a hold.
 *
 * One of the codes the hold reason lookup holds. A hold that names none, or a
 * code the lookup does not hold, is refused with the codes it accepts, so a
 * held request always says what it waits for in words a report can group by.
 * Empty on every decision that is not a hold.
 */

3.6. decide_approval_request_response

3.6.1. result

3.6.2. request

/**
 * @brief The request after the decision, when the outcome is ok.
 */

3.7. list_approval_queue_request

/**
 * @brief Reads the open requests the signed-in person may decide, and the ones
 * answered within the installation's window.
 *
 * Open means waiting or held. A request is in the queue when the person holds
 * its kind's decide permission and did not raise it. Oldest first.
 *
 * A request leaves the queue the moment it is answered, which leaves the person
 * who answered it nothing to look at: the notice that says what happened links
 * to a request the queue no longer holds. So the answer carries the requests
 * answered recently beside the open ones, newest answer first, for as long as
 * the installation says. How long is the variability system setting
 * =inbox.approval_queue.answered_window_seconds=; the read falls back to an
 * empty tail when that setting cannot be read, because the open queue is the
 * read's reason to exist and it is not.
 */

3.7.1. offset

3.7.2. limit

3.8. list_approval_queue_response

3.8.1. result

3.8.2. requests

3.8.3. total

/**
 * @brief How many open requests the whole queue holds, for paging. The answered
 * tail is bounded by the window and is not part of this count.
 */

3.8.4. answered

/**
 * @brief The requests answered within the window, newest answer first. Bounded,
 * and not paged: nobody acts on them.
 */

3.9. list_my_approval_requests_request

/**
 * @brief Reads the requests the signed-in person raised, newest first.
 */

3.9.1. offset

3.9.2. limit

3.10. list_my_approval_requests_response

3.10.1. result

3.10.2. requests

3.10.3. total

3.11. get_approvals_mode_request

/**
 * @brief Reads whether the caller's tenant enforces approvals, and who said so.
 *
 * Any signed-in person may ask, so that a request which did not wait can be
 * explained to whoever is looking at it.
 */

3.12. get_approvals_mode_response

3.12.1. result

3.12.2. mode

Either enforced or auto_approved.

3.12.3. scope

Who states the mode: tenant or installation.

3.12.4. set_by

3.12.5. set_at

3.12.6. reason

3.12.7. version

3.13. set_approvals_mode_request

/**
 * @brief Turns auto-approval on or off for the caller's tenant.
 *
 * Needs inbox::approvals:set_mode. Each change is a new version of the setting,
 * with who made it and why.
 */

3.13.1. auto_approved

3.13.2. reason

3.14. set_approvals_mode_response

3.14.1. result

3.15. get_approval_request

/**
 * @brief Reads the one request an identifier names.
 *
 * A notice a person is given carries the request it is about, so the notice
 * has to be able to open it. The queue cannot answer that: it holds what is
 * waiting, and a notice is usually read after the request stopped waiting.
 * The request a person may open is therefore read on its own, and the caller
 * states which request they mean rather than reading a list to find it.
 *
 * What the caller may open is the store's to decide: the person who asked,
 * and whoever may decide a request of that kind. A stranger is answered as if
 * the request did not exist, so the read tells nobody that a request they may
 * not see is there.
 */

3.15.1. request_id

3.16. get_approval_response

3.16.1. result

3.16.2. request

/**
 * @brief The request, present when the caller may open it.
 *
 * Absent means one thing to the caller and two things to the store: no such
 * request, or a request this caller may not see. Telling them apart would
 * tell a stranger that a request exists.
 */

3.17. expire_overdue_approvals_request

/**
 * @brief Closes every open request past its kind's deadline, across every
 * tenant.
 *
 * The scheduler fires this, so it acts as the service rather than as a person
 * and reaches requests no tenant-scoped caller could read. Each person who
 * asked is told; a request already closed is not open, so a repeated call
 * changes nothing.
 *
 * A scheduler firing is a plain publish with no token, so this carries no
 * session: it is trusted at the transport, as the compute reaper is. That is
 * affordable here because it takes no input and closes only what is already
 * past its deadline, which a caller could reach by waiting.
 */

3.18. expire_overdue_approvals_response

3.18.1. result

3.18.2. expired

/**
 * @brief The requests that closed, as UUID strings, oldest deadline first.
 */

3.19. remind_expiring_approvals_request

/**
 * @brief Warns the deciders of every open request whose deadline is close.
 *
 * The other half of the deadline. The sweep that closes what nobody answered
 * keeps the queue moving, but a decider who never looked learns afterwards;
 * this gives them the chance the deadline was there to give.
 *
 * It has its own schedule and its own window, because warning somebody is a
 * nudge with its own timing and its own audience, while closing a queue that
 * has stopped moving is housekeeping. The window is the variability setting
 * =inbox.approval_expiry.reminder_window_seconds=.
 *
 * A request is warned about once, and the notice is what says so: a reminder
 * already raised names the request, so there is no second mark to keep in step
 * with the first. A request answered before its deadline passes stops matching,
 * and the one the sweep closed is left out by the close.
 *
 * A scheduler firing is a plain publish with no token, so this carries no
 * session: it takes no input, and it only reads requests that are already close
 * to a deadline their kind set.
 */

3.20. remind_expiring_approvals_response

3.20.1. result

3.20.2. reminded

/**
 * @brief The requests complained about, as UUID strings, nearest deadline
 * first.
 */

3.21. retry_stalled_applies_request

/**
 * @brief Resumes the applies that stopped, and ends the ones that ran out of
 * time.
 *
 * An approved request whose kind names an apply workflow stays approved until
 * the workflow applies it. When the run stopped on a failure that can pass, or
 * the start was lost, nothing moves it on. This finds those requests, starts
 * the run again (a repeated start names the same run, so it changes nothing when
 * the run exists) and asks the engine to resume a stopped run. A run that
 * stopped on a refusal never reaches here: the refusal moves its request to
 * apply_failed.
 *
 * When the kind's window ends, the request moves to apply_failed with the
 * reason "could not apply", and the person who asked is told, so a request never
 * retries forever.
 *
 * A scheduler firing is a plain publish with no token, so this carries no
 * session: it takes no input, and it only acts on requests that are already
 * approved.
 */

3.22. retry_stalled_applies_response

3.22.1. result

3.22.2. resumed

/**
 * @brief The requests whose stopped run the engine resumed, as UUID strings.
 */

3.22.3. gave_up

/**
 * @brief The requests that moved to apply_failed because the window ended, as
 * UUID strings.
 */

3.23. approval_request_field

/**
 * @brief One field of a request at one version, as a screen names and draws it.
 *
 * The value is text whatever its type: the renderer that fills this in is the
 * same one the record screens' history panel reads, so a story and a panel
 * draw the same request the same way.
 */

3.23.1. name

3.23.2. value

3.24. approval_request_version

/**
 * @brief One version of a request, with who wrote it, when, and every field it
 * held.
 *
 * The oldest version is the request as it was raised. Each later one is a
 * decision that moved it, so this is the request's own half of its story. The
 * screen pairs each version with the one before it to draw what changed.
 */

3.24.1. version

3.24.2. modified_by

/**
 * @brief The person or service that wrote this version.
 */

3.24.3. performed_by

3.24.4. recorded_at

/**
 * @brief When this version was written, which for an answered request is the
 * moment of the answer.
 */

3.24.5. change_reason_code

3.24.6. change_commentary

3.24.7. fields

3.25. get_approval_history_request

/**
 * @brief Reads every version of one request, newest first.
 *
 * Answered when the caller raised the request, and when the caller may read
 * approval requests. Answered as not found otherwise, so a caller learns
 * nothing about a request they may not read.
 *
 * The entity read of approval requests and the generic history read both gate
 * on the administrator's permission, so neither can answer the person who
 * raised the request with their own request's versions. This is the read that
 * can, and it is why the story of a request is an operation rather than a
 * composition of the reads that already exist.
 */

3.25.1. request_id

3.26. get_approval_history_response

3.26.1. result

3.26.2. versions

3.27. approval_part_progress

/**
 * @brief Where one part of a request stands.
 *
 * A request that needs several functions to approve lists one of these for each.
 * The state says what a person reading the request needs to know: whether the
 * part has answered, whether its turn has come, or whether an earlier part
 * must answer first.
 */

3.27.1. code

3.27.2. name

3.27.3. answer_order

3.27.4. state

/**
 * @brief approved, refused, waiting (its turn has come) or later (an earlier part
 * must answer first).
 */

3.27.5. decided_by

/**
 * @brief The account that answered the part, as a UUID string, or empty.
 */

3.27.6. decided_at

3.27.7. comment

3.28. get_approval_parts_request

/**
 * @brief Reads where each part of one request stands.
 *
 * Answered when the caller raised the request, and when the caller may decide
 * a request of its kind. Answered as not found otherwise, as opening the
 * request is. The person who asked is the one who waits on the parts, so the
 * entity read of the parts, which gates on the administrator's permission,
 * cannot serve them.
 */

3.28.1. request_id

3.29. get_approval_parts_response

3.29.1. result

3.29.2. parts

/**
 * @brief The parts in the order they answer in. Empty for a request that names
 * no parts.
 */

4. See also

Emacs 29.3 (Org mode 9.6.15)