phase 14-15: docker build validation and helm deployment
This commit is contained in:
@@ -1,29 +1,84 @@
|
||||
"""Base adapter interface for all external API integrations."""
|
||||
"""Base adapter interface for all external API integrations.
|
||||
|
||||
All ingestion adapters follow the same contract:
|
||||
1. Fetch external payloads for a given ticker/source config.
|
||||
2. Return a structured result with raw bytes, parsed items, and metadata.
|
||||
3. The ingestion worker handles MinIO upload, PostgreSQL metadata, and downstream job emission.
|
||||
|
||||
Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 3.1, 3.2, 3.3, 3.4
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any
|
||||
|
||||
|
||||
@dataclass
|
||||
class AdapterResult:
|
||||
"""Result of a single adapter fetch operation."""
|
||||
|
||||
source_type: str
|
||||
ticker: str
|
||||
items: List[Dict[str, Any]]
|
||||
items: list[dict[str, Any]]
|
||||
raw_payload: bytes
|
||||
content_hash: str
|
||||
fetched_at: datetime
|
||||
error: Optional[str] = None
|
||||
error: str | None = None
|
||||
# HTTP metadata for observability
|
||||
http_status: int | None = None
|
||||
response_time_ms: float | None = None
|
||||
# Additional metadata the adapter wants to pass downstream
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
@property
|
||||
def ok(self) -> bool:
|
||||
"""True if the fetch succeeded without error."""
|
||||
return self.error is None and len(self.items) > 0
|
||||
|
||||
@property
|
||||
def item_count(self) -> int:
|
||||
return len(self.items)
|
||||
|
||||
|
||||
class BaseAdapter(ABC):
|
||||
"""Interface for all ingestion adapters."""
|
||||
"""Interface for all ingestion adapters.
|
||||
|
||||
Subclasses implement fetch() for their specific API and source_type()
|
||||
to identify the adapter class. The ingestion worker orchestrates
|
||||
persistence and downstream job emission.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
async def fetch(self, ticker: str, config: Dict[str, Any]) -> AdapterResult:
|
||||
"""Fetch data for a given ticker using source config."""
|
||||
async def fetch(self, ticker: str, config: dict[str, Any]) -> AdapterResult:
|
||||
"""Fetch data for a given ticker using source config.
|
||||
|
||||
Args:
|
||||
ticker: The company ticker symbol.
|
||||
config: Source-specific configuration from the sources table.
|
||||
|
||||
Returns:
|
||||
AdapterResult with raw payload, parsed items, and metadata.
|
||||
"""
|
||||
...
|
||||
|
||||
@abstractmethod
|
||||
def source_type(self) -> str:
|
||||
"""Return the source type identifier for this adapter (e.g. 'market_api')."""
|
||||
...
|
||||
|
||||
def bucket_name(self) -> str:
|
||||
"""Return the MinIO bucket name for raw artifact storage.
|
||||
|
||||
Override in subclasses if the bucket differs from the default pattern.
|
||||
"""
|
||||
return f"stonks-raw-{self.source_type().replace('_api', '').replace('_', '-')}"
|
||||
|
||||
def artifact_path(self, ticker: str, document_id: str, now: datetime) -> str:
|
||||
"""Build the MinIO object path for a raw artifact.
|
||||
|
||||
Pattern: /{source_type}/{ticker}/{yyyy}/{mm}/{dd}/{document_id}/raw.json
|
||||
"""
|
||||
return (
|
||||
f"{self.source_type()}/{ticker}/"
|
||||
f"{now.strftime('%Y/%m/%d')}/{document_id}/raw.json"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user