fix(manual-ledger): harden schema init race and sanitize error responses
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
co-authored by
genichin
parent
78e347703e
commit
a36b451a3d
@@ -736,6 +736,22 @@ def _extract_duplicate_dividend_entry_id(message: str) -> int | None:
|
||||
return None
|
||||
|
||||
|
||||
def _manual_error_message(code: str) -> str:
|
||||
if code == "LEDGER_ENTRY_NOT_FOUND":
|
||||
return "Target ledger entry not found"
|
||||
if code == "POTENTIAL_DUPLICATE_DIVIDEND":
|
||||
return "Potential duplicate dividend entry detected"
|
||||
if code == "INSUFFICIENT_CASH":
|
||||
return "Insufficient cash balance"
|
||||
if code == "INSUFFICIENT_QUANTITY":
|
||||
return "Insufficient asset quantity"
|
||||
if code == "INSUFFICIENT_BUFFER":
|
||||
return "Insufficient profit buffer"
|
||||
if code == "LEDGER_ENTRY_ALREADY_REVERSED":
|
||||
return "Ledger entry is already reversed"
|
||||
return "Manual ledger request could not be processed"
|
||||
|
||||
|
||||
def _encode_ledger_cursor(*, portfolio_id: str, source: str | None, recorded_at: datetime, ledger_entry_id: int) -> str:
|
||||
payload = {
|
||||
"portfolio_id": portfolio_id,
|
||||
@@ -855,17 +871,17 @@ def record_manual_ledger_v1(
|
||||
code = _manual_error_code(message)
|
||||
if code == "LEDGER_ENTRY_NOT_FOUND":
|
||||
status_code = status.HTTP_404_NOT_FOUND
|
||||
detail: str | dict[str, object] = {"code": code, "message": message}
|
||||
detail: str | dict[str, object] = {"code": code, "message": _manual_error_message(code)}
|
||||
elif code == "POTENTIAL_DUPLICATE_DIVIDEND":
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
detail = {
|
||||
"code": code,
|
||||
"message": "Potential duplicate dividend entry detected",
|
||||
"message": _manual_error_message(code),
|
||||
"matched_ledger_entry_id": _extract_duplicate_dividend_entry_id(message),
|
||||
}
|
||||
else:
|
||||
status_code = status.HTTP_409_CONFLICT if code != "MANUAL_LEDGER_CONFLICT" else status.HTTP_422_UNPROCESSABLE_ENTITY
|
||||
detail = {"code": code, "message": message}
|
||||
detail = {"code": code, "message": _manual_error_message(code)}
|
||||
raise HTTPException(status_code=status_code, detail=detail) from exc
|
||||
finally:
|
||||
_close_repo(repo)
|
||||
|
||||
@@ -206,6 +206,7 @@ class PostgresPortfolioRepository:
|
||||
def ensure_schema(self) -> None:
|
||||
conn = self._connect()
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT pg_advisory_lock(hashtext('portfoliohub.ensure_schema.v1'))")
|
||||
cur.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
@@ -507,6 +508,7 @@ class PostgresPortfolioRepository:
|
||||
SELECT create_hypertable('portfolio_ledger', 'recorded_at', if_not_exists => TRUE)
|
||||
"""
|
||||
)
|
||||
cur.execute("SELECT pg_advisory_unlock(hashtext('portfoliohub.ensure_schema.v1'))")
|
||||
|
||||
@staticmethod
|
||||
def _row_to_user(row: Any) -> User:
|
||||
|
||||
Reference in New Issue
Block a user