fix(accounts): register /accounts/hierarchy before /accounts/{account_id}
FastAPI matches in registration order, and hierarchy was registered
~3,300 lines after the {account_id} route — every request resolved as
account_id="hierarchy" and 404'd, so the endpoint has been unreachable
since it was added. Moved above the param routes in the accounts
module, with a functional reachability test and an updated route
snapshot (literal-before-param is now asserted for both overlap
families).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a7d7740a3a
commit
a0166d4026
2 changed files with 105 additions and 88 deletions
|
|
@ -171,84 +171,10 @@ async def api_create_account(
|
|||
)
|
||||
|
||||
|
||||
@router.get("/api/v1/accounts/{account_id}")
|
||||
async def api_get_account(
|
||||
account_id: str,
|
||||
auth: AuthContext = Depends(require_authenticated),
|
||||
) -> Account:
|
||||
"""Get a specific account (requires authentication and account access)"""
|
||||
account = await get_account(account_id)
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="Account not found"
|
||||
)
|
||||
# Check access permission
|
||||
await require_account_access(auth, account_id, PermissionType.READ)
|
||||
return account
|
||||
|
||||
|
||||
@router.get("/api/v1/accounts/{account_id}/balance")
|
||||
async def api_get_account_balance(
|
||||
account_id: str,
|
||||
auth: AuthContext = Depends(require_authenticated),
|
||||
) -> dict:
|
||||
"""Get account balance from Fava/Beancount (requires authentication and account access)"""
|
||||
from ..fava_client import get_fava_client
|
||||
|
||||
# Get account to retrieve its name
|
||||
account = await get_account(account_id)
|
||||
if not account:
|
||||
raise HTTPException(status_code=404, detail="Account not found")
|
||||
|
||||
# Check access permission
|
||||
await require_account_access(auth, account_id, PermissionType.READ)
|
||||
|
||||
# Query Fava for balance
|
||||
fava = get_fava_client()
|
||||
balance_data = await fava.get_account_balance(account.name)
|
||||
|
||||
return {
|
||||
"account_id": account_id,
|
||||
"balance": balance_data["sats"], # Balance in satoshis
|
||||
"fiat": float(balance_data.get("fiat", 0)), # Fiat amount
|
||||
"fiat_currency": balance_data.get("fiat_currency", "EUR")
|
||||
}
|
||||
|
||||
|
||||
@router.get("/api/v1/accounts/{account_id}/transactions")
|
||||
async def api_get_account_transactions(
|
||||
account_id: str,
|
||||
limit: int = 100,
|
||||
auth: AuthContext = Depends(require_authenticated),
|
||||
) -> list[dict]:
|
||||
"""
|
||||
Get all transactions for an account from Fava/Beancount.
|
||||
|
||||
Returns transactions affecting this account in reverse chronological order.
|
||||
Requires authentication and account access.
|
||||
"""
|
||||
from ..fava_client import get_fava_client
|
||||
|
||||
# Get account details
|
||||
account = await get_account(account_id)
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail=f"Account {account_id} not found"
|
||||
)
|
||||
|
||||
# Check access permission
|
||||
await require_account_access(auth, account_id, PermissionType.READ)
|
||||
|
||||
# Query Fava for transactions
|
||||
fava = get_fava_client()
|
||||
transactions = await fava.get_account_transactions(account.name, limit)
|
||||
|
||||
return transactions
|
||||
|
||||
|
||||
# ===== JOURNAL ENTRY ENDPOINTS =====
|
||||
|
||||
# NOTE: hierarchy must be registered BEFORE /accounts/{account_id} —
|
||||
# FastAPI matches in registration order, and the param route otherwise
|
||||
# swallows the literal path (it resolved as account_id="hierarchy" and
|
||||
# 404'd for as long as the endpoint existed).
|
||||
@router.get("/api/v1/accounts/hierarchy")
|
||||
async def api_get_account_hierarchy(
|
||||
root_account: str | None = None,
|
||||
|
|
@ -326,3 +252,82 @@ async def api_get_account_hierarchy(
|
|||
|
||||
|
||||
# ===== ACCOUNT SYNC ENDPOINTS =====
|
||||
|
||||
|
||||
@router.get("/api/v1/accounts/{account_id}")
|
||||
async def api_get_account(
|
||||
account_id: str,
|
||||
auth: AuthContext = Depends(require_authenticated),
|
||||
) -> Account:
|
||||
"""Get a specific account (requires authentication and account access)"""
|
||||
account = await get_account(account_id)
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND, detail="Account not found"
|
||||
)
|
||||
# Check access permission
|
||||
await require_account_access(auth, account_id, PermissionType.READ)
|
||||
return account
|
||||
|
||||
|
||||
@router.get("/api/v1/accounts/{account_id}/balance")
|
||||
async def api_get_account_balance(
|
||||
account_id: str,
|
||||
auth: AuthContext = Depends(require_authenticated),
|
||||
) -> dict:
|
||||
"""Get account balance from Fava/Beancount (requires authentication and account access)"""
|
||||
from ..fava_client import get_fava_client
|
||||
|
||||
# Get account to retrieve its name
|
||||
account = await get_account(account_id)
|
||||
if not account:
|
||||
raise HTTPException(status_code=404, detail="Account not found")
|
||||
|
||||
# Check access permission
|
||||
await require_account_access(auth, account_id, PermissionType.READ)
|
||||
|
||||
# Query Fava for balance
|
||||
fava = get_fava_client()
|
||||
balance_data = await fava.get_account_balance(account.name)
|
||||
|
||||
return {
|
||||
"account_id": account_id,
|
||||
"balance": balance_data["sats"], # Balance in satoshis
|
||||
"fiat": float(balance_data.get("fiat", 0)), # Fiat amount
|
||||
"fiat_currency": balance_data.get("fiat_currency", "EUR")
|
||||
}
|
||||
|
||||
|
||||
@router.get("/api/v1/accounts/{account_id}/transactions")
|
||||
async def api_get_account_transactions(
|
||||
account_id: str,
|
||||
limit: int = 100,
|
||||
auth: AuthContext = Depends(require_authenticated),
|
||||
) -> list[dict]:
|
||||
"""
|
||||
Get all transactions for an account from Fava/Beancount.
|
||||
|
||||
Returns transactions affecting this account in reverse chronological order.
|
||||
Requires authentication and account access.
|
||||
"""
|
||||
from ..fava_client import get_fava_client
|
||||
|
||||
# Get account details
|
||||
account = await get_account(account_id)
|
||||
if not account:
|
||||
raise HTTPException(
|
||||
status_code=HTTPStatus.NOT_FOUND,
|
||||
detail=f"Account {account_id} not found"
|
||||
)
|
||||
|
||||
# Check access permission
|
||||
await require_account_access(auth, account_id, PermissionType.READ)
|
||||
|
||||
# Query Fava for transactions
|
||||
fava = get_fava_client()
|
||||
transactions = await fava.get_account_transactions(account.name, limit)
|
||||
|
||||
return transactions
|
||||
|
||||
|
||||
# ===== JOURNAL ENTRY ENDPOINTS =====
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue