app.services.intake¶
CI push intake: PUT / POST /api/v1/candidates.
A pushed document goes through the same parse -> validate -> hash ->
persist path as a polled one (app.services.ingest), always as a
candidate row: a push can never write, replace or delete a
published row, and never touches approval history. Source adapters are
never called.
Checks, in this order (each failure is answered with a stable code
and a hint; see docs/the router for the HTTP status):
no bearer token -> 401
not_authenticated;the token is not a push token of the named registration (unknown or disabled registrations have none) -> 401
invalid_push_token. Authentication comes first, so an unauthenticated caller learns nothing about which registrations exist or what the body should contain (a request without a usablesource_idis authenticated against every registration before it is refused);body larger than
MAX_BODY_BYTES-> 413payload_too_large;malformed body (not JSON, missing/extra/invalid fields; a client-supplied
digestis an unexpected field) -> 422invalid_request;registration lacks
pushintake -> 403push_disabled;project outside the registration scope -> 403
project_out_of_scope;refequals the configuredpublished_ref-> 422published_ref_not_allowed. A leadingrefs/heads/is ignored on both sides. Forpublished_ref: "@project-default"(GitLab) the real default branch is unknown without a source call, which intake never makes: only the literal value is refused, and a push to the default branch is stored as a candidate (documented limitation; polling still owns the published row);delivery_idalready received for the registration -> the earlier result (deduplicated: true), nothing is re-ingested (checked again under the registration lock against races);content larger than
MAX_CONTENT_BYTES(UTF-8) -> 413content_too_large; content that cannot be encoded as UTF-8 (lone surrogates) -> 422bad_encoding;document without usable frontmatter or UUID -> 422
invalid_documentwith theissueslist; document UUID other thanadr_uuid-> 422uuid_mismatch;POSTand the candidate(source_id, project, adr_uuid, ref)exists -> 409candidate_exists.
Every request records an intake_run (kind push): successes
(also deduplicated ones) with the outcome in counts, failures with
errors. The token is never stored; source_id is stored as
requested (truncated), possibly naming no registration.
delivery_id is stored only on the successful run, so a failed
delivery can be retried with the same id.
Public API: MAX_BODY_BYTES, MAX_CONTENT_BYTES and
CandidateSubmission (re-exported from
app.services.intake_checks), IntakeMode,
IntakeResult, submit_candidate().
- class app.services.intake.CandidateSubmission(*, source_id, project, adr_uuid, ref, content, path=None, delivery_id=None, author=None)¶
Bases:
BaseModelBody of
PUT/POST /api/v1/candidates; extra keys -> 422.
- class app.services.intake.IntakeMode(*values)¶
Bases:
StrEnumPUT(create or replace) orPOST(create only).- REPLACE = 'put'¶
- CREATE = 'post'¶
- class app.services.intake.IntakeResult(*, record_id, digest, created, updated, deduplicated, issues)¶
Bases:
objectOutcome of one candidate submission.
- Parameters:
record_id (
UUID) – Candidate row id.digest (
str) – Server-computed canonical digest.created (
bool) – A new candidate row was inserted.updated (
bool) – An existing candidate got new content.deduplicated (
bool) – The delivery id was seen before; nothing was ingested and this is the earlier result.issues (
tuple[Issue,...]) – Per-document validation issues (non-blocking).
- app.services.intake.submit_candidate(provider, sources, *, token, body, mode)¶
Authenticate, validate and ingest one pushed candidate.
Blocking (synchronous database calls): call it from a worker thread.
- Parameters:
provider (
CatalogStoreProvider) – Storage provider.sources (
Sequence[SourceRegistration]) – Configured registrations.body (
bytes) – Raw request body (read with a cap slightly aboveMAX_BODY_BYTES).mode (
IntakeMode) –PUTorPOSTsemantics.
- Return type:
- Returns:
The outcome.
- Raises:
app.errors.AdrianoError – The refusals listed in the module docstring; each one is recorded as a failed
intake_run.