fix: enable dev SRO collection and handle real lookup responses
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 3m12s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 4m0s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 2m44s
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 3m12s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 4m0s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 2m44s
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
"""Permission-gated, paced reads of the SRO site; no automatic hidden retries."""
|
||||
"""Access-gated SRO reads with explicit dev opt-in, pacing and bounded retries."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from email.utils import parsedate_to_datetime
|
||||
from urllib.parse import urljoin, urlsplit
|
||||
from urllib.parse import parse_qs, urljoin, urlsplit
|
||||
|
||||
import requests
|
||||
from apps.core.exceptions import ConflictError
|
||||
@@ -16,6 +17,8 @@ from django.conf import settings
|
||||
|
||||
|
||||
def require_sro_access_approved() -> None:
|
||||
if settings.SRO_DEV_COLLECTION_ENABLED:
|
||||
return
|
||||
if not (
|
||||
settings.SRO_UPSTREAM_ACCESS_APPROVED
|
||||
and settings.SRO_UPSTREAM_APPROVAL_REFERENCE.strip()
|
||||
@@ -47,10 +50,26 @@ def safe_sro_url(value: str, *, base: str | None = None) -> str:
|
||||
return value
|
||||
|
||||
|
||||
def is_sro_lookup_url(value: str) -> bool:
|
||||
"""Only the observed exact identifier lookup may carry a negative 404 body."""
|
||||
try:
|
||||
parts = urlsplit(safe_sro_url(value))
|
||||
query = parse_qs(parts.query, keep_blank_values=True, max_num_fields=20)
|
||||
except (SnapshotValidationError, ValueError):
|
||||
return False
|
||||
identifiers = query.get("q", [])
|
||||
return (
|
||||
parts.path == "/proverka_dopuska/"
|
||||
and len(identifiers) == 1
|
||||
and re.fullmatch(r"[0-9]{10}(?:[0-9]{3})?", identifiers[0]) is not None
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SroPage:
|
||||
url: str
|
||||
body: bytes
|
||||
status_code: int = 200
|
||||
|
||||
|
||||
class SroHttpClient:
|
||||
@@ -62,9 +81,7 @@ class SroHttpClient:
|
||||
self.owns_session = session is None
|
||||
if self.owns_session:
|
||||
self.session.trust_env = False
|
||||
self.session.headers[
|
||||
"User-Agent"
|
||||
] = "Mostovik SRO integration (approved access)"
|
||||
self.session.headers["User-Agent"] = "Mostovik SRO integration"
|
||||
self.clock, self.sleep = clock, sleep
|
||||
self.last_request = None
|
||||
self.requests_count = 0
|
||||
@@ -79,11 +96,14 @@ class SroHttpClient:
|
||||
url = safe_sro_url(response.headers.get("Location", ""), base=url)
|
||||
continue
|
||||
if response.status_code == 404:
|
||||
raise SnapshotValidationError("sro_page_not_found")
|
||||
if response.status_code != 200:
|
||||
if not is_sro_lookup_url(url):
|
||||
raise SnapshotValidationError("sro_page_not_found")
|
||||
elif response.status_code != 200:
|
||||
raise SnapshotValidationError("sro_upstream_http_error")
|
||||
return SroPage(
|
||||
url, limited_bytes(response, settings.SRO_HTTP_MAX_RESPONSE_BYTES)
|
||||
url,
|
||||
limited_bytes(response, settings.SRO_HTTP_MAX_RESPONSE_BYTES),
|
||||
status_code=response.status_code,
|
||||
)
|
||||
except requests.RequestException as exc:
|
||||
self.http_errors_count += 1
|
||||
|
||||
Reference in New Issue
Block a user