aeat.adapters.outbound.aeat.browser._site_health module

Strict records for AEAT site-health detection.

The site-health layer classifies AEAT Sede Electrónica responses into a closed SiteHealthState catalogue. A planned mantenimiento page, WAF challenge, HTTP 429/503 rate-limit answer, or transport failure becomes a typed SiteHealthStatus instead of collapsing into a generic browser or workflow exception.

Every record in this module is frozen, strict, extra="forbid" pydantic v2. The closed state catalogue is an enum.StrEnum. Collections are tuple[str, ...] so evidence remains immutable after classification.

See also

adapters.outbound.aeat.browser._site_health_parsers.evaluate_response()

Pure parser entry point that creates SiteHealthStatus records from HTTP status, headers, and body text.

adapters.outbound.aeat.browser.BrowserSession.navigate()

Browser navigation hook that raises core.errors.SiteHealthError when a non-OK status is classified.

core.errors.SiteHealthStatusLike

Core-layer structural view that lets application workflow and diagnostics consume these adapter records without importing them.

class SiteHealthState(*values)[source]

Bases: StrEnum

Closed catalogue of AEAT site-health classifications.

Parser functions emit these states inside SiteHealthStatus. SiteHealthError then carries non-OK states across the browser/application boundary.

Values:
OK: No anomaly detected; the response looks like a healthy

AEAT page.

MANTENIMIENTO: A scheduled / unplanned maintenance banner or

interstitial was detected.

WAF_CHALLENGE: A Web Application Firewall rejection or

challenge page (Request blocked, reference / support IDs, etc.).

RATE_LIMITED: Server answered with HTTP 429 or 503 indicating

the caller is being throttled.

UNREACHABLE: Transport-level failure (DNS, TCP, TLS,

Playwright navigation timeout) before a response body could be inspected.

UNKNOWN_ERROR: Reserved for future unclassified failures; no

parser currently emits this state.

OK
MANTENIMIENTO
WAF_CHALLENGE
RATE_LIMITED
UNREACHABLE
UNKNOWN_ERROR
class SiteHealthEvidence(**data)[source]

Bases: _SiteHealthRecord

Bounded evidence block captured alongside a classification.

Evidence is diagnostic, not a full response archive. The HTML fragment is redacted and bounded before it is stored, while detected_markers keeps the exact marker keys that caused the parser to choose a SiteHealthState.

Variables:
  • url – The probe URL whose response was classified.

  • http_status – HTTP status observed on the response. Bounded to [100, 599]; transport-level failures use 599 as a sentinel and annotate detected_markers accordingly.

  • html_fragment – At most 4096 characters of redacted response body, captured for diagnostic logs. The bound keeps error logs sane and avoids leaking full response bodies.

  • detected_markers – Frozen tuple of the substrings (or structured keys such as retry-after:30) that led the parser to its verdict. Each marker is bounded between 1 and 128 characters.

Parameters:
  • url (AnyHttpUrl)

  • http_status (int)

  • html_fragment (str)

  • detected_markers (tuple[str, ...])

url: AnyHttpUrl
http_status: int
html_fragment: str
detected_markers: tuple[str, ...]
class SiteHealthStatus(**data)[source]

Bases: _SiteHealthRecord

Full classification record for a single probe.

Produced by the parser suite and by adapters.outbound.aeat.browser.BrowserSession.navigate() for transport failures. Non-OK records are carried by core.errors.SiteHealthError; diagnostics and workflow code then inspect this record instead of re-parsing response bodies.

Variables:
  • state – The detected SiteHealthState.

  • evidence – Bounded SiteHealthEvidence describing the response that was classified.

  • observed_at – Timezone-aware timestamp when the classification was produced.

  • retry_after_seconds – Optional positive integer derived from a Retry-After header or the rate-limit default when the state is SiteHealthState.RATE_LIMITED.

Parameters:
state: SiteHealthState
evidence: SiteHealthEvidence
observed_at: AwareDatetime
retry_after_seconds: int | None