Based on the repository's commit message style and the changes in the diff, here's an appropriate commit message:
``` feat: add websocket tests, PoA metrics, marketplace endpoints, and enhanced observability - Add comprehensive websocket tests for blocks and transactions streams including multi-subscriber and high-volume scenarios - Extend PoA consensus with per-proposer block metrics and rotation tracking - Add latest block interval gauge and RPC error spike alerting - Enhance mock coordinator
This commit is contained in:
@@ -1 +1,9 @@
|
||||
"""Router modules for the coordinator API."""
|
||||
|
||||
from .client import router as client
|
||||
from .miner import router as miner
|
||||
from .admin import router as admin
|
||||
from .marketplace import router as marketplace
|
||||
from .explorer import router as explorer
|
||||
|
||||
__all__ = ["client", "miner", "admin", "marketplace", "explorer"]
|
||||
|
||||
63
apps/coordinator-api/src/app/routers/explorer.py
Normal file
63
apps/coordinator-api/src/app/routers/explorer.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
|
||||
from ..models import (
|
||||
BlockListResponse,
|
||||
TransactionListResponse,
|
||||
AddressListResponse,
|
||||
ReceiptListResponse,
|
||||
)
|
||||
from ..services import ExplorerService
|
||||
from ..storage import SessionDep
|
||||
|
||||
router = APIRouter(prefix="/explorer", tags=["explorer"])
|
||||
|
||||
|
||||
def _service(session: SessionDep) -> ExplorerService:
|
||||
return ExplorerService(session)
|
||||
|
||||
|
||||
@router.get("/blocks", response_model=BlockListResponse, summary="List recent blocks")
|
||||
async def list_blocks(
|
||||
*,
|
||||
session: SessionDep,
|
||||
limit: int = Query(default=20, ge=1, le=200),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
) -> BlockListResponse:
|
||||
return _service(session).list_blocks(limit=limit, offset=offset)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/transactions",
|
||||
response_model=TransactionListResponse,
|
||||
summary="List recent transactions",
|
||||
)
|
||||
async def list_transactions(
|
||||
*,
|
||||
session: SessionDep,
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
) -> TransactionListResponse:
|
||||
return _service(session).list_transactions(limit=limit, offset=offset)
|
||||
|
||||
|
||||
@router.get("/addresses", response_model=AddressListResponse, summary="List address summaries")
|
||||
async def list_addresses(
|
||||
*,
|
||||
session: SessionDep,
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
) -> AddressListResponse:
|
||||
return _service(session).list_addresses(limit=limit, offset=offset)
|
||||
|
||||
|
||||
@router.get("/receipts", response_model=ReceiptListResponse, summary="List job receipts")
|
||||
async def list_receipts(
|
||||
*,
|
||||
session: SessionDep,
|
||||
job_id: str | None = Query(default=None, description="Filter by job identifier"),
|
||||
limit: int = Query(default=50, ge=1, le=200),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
) -> ReceiptListResponse:
|
||||
return _service(session).list_receipts(job_id=job_id, limit=limit, offset=offset)
|
||||
57
apps/coordinator-api/src/app/routers/marketplace.py
Normal file
57
apps/coordinator-api/src/app/routers/marketplace.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from fastapi import status as http_status
|
||||
|
||||
from ..models import MarketplaceBidRequest, MarketplaceOfferView, MarketplaceStatsView
|
||||
from ..services import MarketplaceService
|
||||
from ..storage import SessionDep
|
||||
|
||||
router = APIRouter(tags=["marketplace"])
|
||||
|
||||
|
||||
def _get_service(session: SessionDep) -> MarketplaceService:
|
||||
return MarketplaceService(session)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/marketplace/offers",
|
||||
response_model=list[MarketplaceOfferView],
|
||||
summary="List marketplace offers",
|
||||
)
|
||||
async def list_marketplace_offers(
|
||||
*,
|
||||
session: SessionDep,
|
||||
status_filter: str | None = Query(default=None, alias="status", description="Filter by offer status"),
|
||||
limit: int = Query(default=100, ge=1, le=500),
|
||||
offset: int = Query(default=0, ge=0),
|
||||
) -> list[MarketplaceOfferView]:
|
||||
service = _get_service(session)
|
||||
try:
|
||||
return service.list_offers(status=status_filter, limit=limit, offset=offset)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=http_status.HTTP_400_BAD_REQUEST, detail="invalid status filter") from None
|
||||
|
||||
|
||||
@router.get(
|
||||
"/marketplace/stats",
|
||||
response_model=MarketplaceStatsView,
|
||||
summary="Get marketplace summary statistics",
|
||||
)
|
||||
async def get_marketplace_stats(*, session: SessionDep) -> MarketplaceStatsView:
|
||||
service = _get_service(session)
|
||||
return service.get_stats()
|
||||
|
||||
|
||||
@router.post(
|
||||
"/marketplace/bids",
|
||||
status_code=http_status.HTTP_202_ACCEPTED,
|
||||
summary="Submit a marketplace bid",
|
||||
)
|
||||
async def submit_marketplace_bid(
|
||||
payload: MarketplaceBidRequest,
|
||||
session: SessionDep,
|
||||
) -> dict[str, str]:
|
||||
service = _get_service(session)
|
||||
bid = service.create_bid(payload)
|
||||
return {"id": bid.id}
|
||||
Reference in New Issue
Block a user