Phase 0.1: Remove backend deprecated code
- Delete routes: counter.py, sum.py - Delete jobs.py and worker.py - Delete tests: test_counter.py, test_jobs.py - Update audit.py: keep only price-history endpoints - Update models.py: remove VIEW_COUNTER, INCREMENT_COUNTER, USE_SUM permissions - Update models.py: remove Counter, SumRecord, CounterRecord, RandomNumberOutcome models - Update schemas.py: remove sum/counter related schemas - Update main.py: remove deleted router imports - Update test_permissions.py: remove tests for deprecated features - Update test_price_history.py: remove worker-related tests - Update conftest.py: remove mock_enqueue_job fixture - Update auth.py: fix example in docstring
This commit is contained in:
parent
ea85198171
commit
5bad1e7e17
14 changed files with 35 additions and 1393 deletions
|
|
@ -1,98 +1,18 @@
|
|||
"""Audit routes for viewing action records."""
|
||||
"""Audit routes for price history."""
|
||||
|
||||
from collections.abc import Callable
|
||||
from typing import TypeVar
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import desc, func, select
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy import desc, select
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from auth import require_permission
|
||||
from database import get_db
|
||||
from models import (
|
||||
CounterRecord,
|
||||
Permission,
|
||||
PriceHistory,
|
||||
RandomNumberOutcome,
|
||||
SumRecord,
|
||||
User,
|
||||
)
|
||||
from pagination import (
|
||||
calculate_offset,
|
||||
calculate_total_pages,
|
||||
create_paginated_response,
|
||||
)
|
||||
from models import Permission, PriceHistory, User
|
||||
from price_fetcher import PAIR_BTC_EUR, SOURCE_BITFINEX, fetch_btc_eur_price
|
||||
from schemas import (
|
||||
CounterRecordResponse,
|
||||
PaginatedCounterRecords,
|
||||
PaginatedSumRecords,
|
||||
PriceHistoryResponse,
|
||||
RandomNumberOutcomeResponse,
|
||||
SumRecordResponse,
|
||||
)
|
||||
from schemas import PriceHistoryResponse
|
||||
|
||||
router = APIRouter(prefix="/api/audit", tags=["audit"])
|
||||
|
||||
R = TypeVar("R", bound=BaseModel)
|
||||
|
||||
|
||||
async def paginate_with_user_email(
|
||||
db: AsyncSession,
|
||||
model: type[SumRecord] | type[CounterRecord],
|
||||
page: int,
|
||||
per_page: int,
|
||||
row_mapper: Callable[..., R],
|
||||
) -> tuple[list[R], int, int]:
|
||||
"""
|
||||
Generic pagination helper for audit records that need user email.
|
||||
|
||||
Returns: (records, total, total_pages)
|
||||
"""
|
||||
# Get total count
|
||||
count_result = await db.execute(select(func.count(model.id)))
|
||||
total = count_result.scalar() or 0
|
||||
|
||||
# Get paginated records with user email
|
||||
offset = calculate_offset(page, per_page)
|
||||
query = (
|
||||
select(model, User.email)
|
||||
.join(User, model.user_id == User.id)
|
||||
.order_by(desc(model.created_at))
|
||||
.offset(offset)
|
||||
.limit(per_page)
|
||||
)
|
||||
result = await db.execute(query)
|
||||
rows = result.all()
|
||||
|
||||
records: list[R] = [row_mapper(record, email) for record, email in rows]
|
||||
return records, total, calculate_total_pages(total, per_page)
|
||||
|
||||
|
||||
def _to_counter_record_response(
|
||||
record: CounterRecord, email: str
|
||||
) -> CounterRecordResponse:
|
||||
return CounterRecordResponse(
|
||||
id=record.id,
|
||||
user_email=email,
|
||||
value_before=record.value_before,
|
||||
value_after=record.value_after,
|
||||
created_at=record.created_at,
|
||||
)
|
||||
|
||||
|
||||
def _to_sum_record_response(record: SumRecord, email: str) -> SumRecordResponse:
|
||||
return SumRecordResponse(
|
||||
id=record.id,
|
||||
user_email=email,
|
||||
a=record.a,
|
||||
b=record.b,
|
||||
result=record.result,
|
||||
created_at=record.created_at,
|
||||
)
|
||||
|
||||
|
||||
def _to_price_history_response(record: PriceHistory) -> PriceHistoryResponse:
|
||||
return PriceHistoryResponse(
|
||||
|
|
@ -105,64 +25,6 @@ def _to_price_history_response(record: PriceHistory) -> PriceHistoryResponse:
|
|||
)
|
||||
|
||||
|
||||
@router.get("/counter", response_model=PaginatedCounterRecords)
|
||||
async def get_counter_records(
|
||||
page: int = Query(1, ge=1),
|
||||
per_page: int = Query(10, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_current_user: User = Depends(require_permission(Permission.VIEW_AUDIT)),
|
||||
) -> PaginatedCounterRecords:
|
||||
"""Get paginated counter action records."""
|
||||
records, total, _ = await paginate_with_user_email(
|
||||
db, CounterRecord, page, per_page, _to_counter_record_response
|
||||
)
|
||||
return create_paginated_response(records, total, page, per_page)
|
||||
|
||||
|
||||
@router.get("/sum", response_model=PaginatedSumRecords)
|
||||
async def get_sum_records(
|
||||
page: int = Query(1, ge=1),
|
||||
per_page: int = Query(10, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_current_user: User = Depends(require_permission(Permission.VIEW_AUDIT)),
|
||||
) -> PaginatedSumRecords:
|
||||
"""Get paginated sum action records."""
|
||||
records, total, _ = await paginate_with_user_email(
|
||||
db, SumRecord, page, per_page, _to_sum_record_response
|
||||
)
|
||||
return create_paginated_response(records, total, page, per_page)
|
||||
|
||||
|
||||
@router.get("/random-jobs", response_model=list[RandomNumberOutcomeResponse])
|
||||
async def get_random_job_outcomes(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_current_user: User = Depends(require_permission(Permission.VIEW_AUDIT)),
|
||||
) -> list[RandomNumberOutcomeResponse]:
|
||||
"""Get all random number job outcomes, newest first."""
|
||||
# Explicit join to avoid N+1 query
|
||||
query = (
|
||||
select(RandomNumberOutcome, User.email)
|
||||
.join(User, RandomNumberOutcome.triggered_by_user_id == User.id)
|
||||
.order_by(desc(RandomNumberOutcome.created_at))
|
||||
)
|
||||
result = await db.execute(query)
|
||||
rows = result.all()
|
||||
|
||||
return [
|
||||
RandomNumberOutcomeResponse(
|
||||
id=outcome.id,
|
||||
job_id=outcome.job_id,
|
||||
triggered_by_user_id=outcome.triggered_by_user_id,
|
||||
triggered_by_email=email,
|
||||
value=outcome.value,
|
||||
duration_ms=outcome.duration_ms,
|
||||
status=outcome.status,
|
||||
created_at=outcome.created_at,
|
||||
)
|
||||
for outcome, email in rows
|
||||
]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Price History Endpoints
|
||||
# =============================================================================
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
"""Counter routes."""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from auth import require_permission
|
||||
from database import get_db
|
||||
from jobs import enqueue_random_number_job
|
||||
from models import Counter, CounterRecord, Permission, User
|
||||
|
||||
router = APIRouter(prefix="/api/counter", tags=["counter"])
|
||||
|
||||
|
||||
async def get_or_create_counter(db: AsyncSession) -> Counter:
|
||||
"""Get the singleton counter, creating it if it doesn't exist."""
|
||||
result = await db.execute(select(Counter).where(Counter.id == 1))
|
||||
counter = result.scalar_one_or_none()
|
||||
if not counter:
|
||||
counter = Counter(id=1, value=0)
|
||||
db.add(counter)
|
||||
await db.commit()
|
||||
await db.refresh(counter)
|
||||
return counter
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def get_counter(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_current_user: User = Depends(require_permission(Permission.VIEW_COUNTER)),
|
||||
) -> dict[str, int]:
|
||||
"""Get the current counter value."""
|
||||
counter = await get_or_create_counter(db)
|
||||
return {"value": counter.value}
|
||||
|
||||
|
||||
@router.post("/increment")
|
||||
async def increment_counter(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(require_permission(Permission.INCREMENT_COUNTER)),
|
||||
) -> dict[str, int]:
|
||||
"""Increment the counter, record the action, and enqueue a random number job."""
|
||||
counter = await get_or_create_counter(db)
|
||||
value_before = counter.value
|
||||
counter.value += 1
|
||||
|
||||
record = CounterRecord(
|
||||
user_id=current_user.id,
|
||||
value_before=value_before,
|
||||
value_after=counter.value,
|
||||
)
|
||||
db.add(record)
|
||||
|
||||
# Enqueue random number job - if this fails, the request fails
|
||||
try:
|
||||
await enqueue_random_number_job(current_user.id)
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=500, detail=f"Failed to enqueue job: {e}"
|
||||
) from e
|
||||
|
||||
await db.commit()
|
||||
return {"value": counter.value}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
"""Sum calculation routes."""
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from auth import require_permission
|
||||
from database import get_db
|
||||
from models import Permission, SumRecord, User
|
||||
from schemas import SumRequest, SumResponse
|
||||
|
||||
router = APIRouter(prefix="/api/sum", tags=["sum"])
|
||||
|
||||
|
||||
@router.post("", response_model=SumResponse)
|
||||
async def calculate_sum(
|
||||
data: SumRequest,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(require_permission(Permission.USE_SUM)),
|
||||
) -> SumResponse:
|
||||
"""Calculate the sum of two numbers and record it."""
|
||||
result = data.a + data.b
|
||||
record = SumRecord(
|
||||
user_id=current_user.id,
|
||||
a=data.a,
|
||||
b=data.b,
|
||||
result=result,
|
||||
)
|
||||
db.add(record)
|
||||
await db.commit()
|
||||
return SumResponse(a=data.a, b=data.b, result=result)
|
||||
Loading…
Add table
Add a link
Reference in a new issue