feat: add Tink API request logger
All checks were successful
Build and Deploy / deploy (push) Successful in 21s

- TinkClient now accepts on_request callback; all API methods log via
  shared _get/_post helpers (method, url, req/resp body, status, timing)
- _logger(sess) helper creates a session-bound callback (max 50 entries)
- All route handlers pass _logger(sess) to _client()
- New GET /demo/log — shows all API calls in reverse-chronological order,
  collapsible req/resp bodies, status + duration badges
- New POST /demo/log/clear — clears the log
- Navbar gets 'API Log' link (always visible)
- _ctx() now applied to ALL template responses (steps 1–6)
This commit is contained in:
Henrik Jess Nielsen
2026-05-22 19:20:33 +02:00
parent a77c709d4d
commit 5e14a219b1
3 changed files with 137 additions and 13 deletions

View File

@@ -326,7 +326,7 @@ async def step4(request: Request):
result = demo_data.MOCK_ACCOUNTS
else:
try:
client = _client()
client = _client(log_cb=_logger(sess))
result = await client.list_accounts(user_token)
# Fall back to demo data if no accounts connected yet
if not result.get("accounts"):
@@ -335,8 +335,7 @@ async def step4(request: Request):
except Exception as e:
error = str(e)
return templates.TemplateResponse("step.html", {
"request": request,
return templates.TemplateResponse("step.html", _ctx(request, {
"step": 4,
"title": "Konti",
"subtitle": "Account List with Balances",
@@ -349,7 +348,7 @@ async def step4(request: Request):
"is_demo": is_demo or (result == demo_data.MOCK_ACCOUNTS),
"next_step": 5,
"prev_step": 3,
})
}))
# ---------------------------------------------------------------------------
@@ -376,7 +375,7 @@ async def step5(request: Request, account_id: Optional[str] = None):
result = demo_data.MOCK_TRANSACTIONS
else:
try:
client = _client()
client = _client(log_cb=_logger(sess))
result = await client.list_transactions(user_token, account_id=account_id)
if not result.get("transactions"):
result = demo_data.MOCK_TRANSACTIONS
@@ -384,8 +383,7 @@ async def step5(request: Request, account_id: Optional[str] = None):
except Exception as e:
error = str(e)
return templates.TemplateResponse("step.html", {
"request": request,
return templates.TemplateResponse("step.html", _ctx(request, {
"step": 5,
"title": "Transaktioner",
"subtitle": "Transaction History",
@@ -398,7 +396,7 @@ async def step5(request: Request, account_id: Optional[str] = None):
"is_demo": is_demo or (result == demo_data.MOCK_TRANSACTIONS),
"next_step": 6,
"prev_step": 4,
})
}))
# ---------------------------------------------------------------------------
@@ -445,7 +443,7 @@ async def step6(request: Request):
}
else:
try:
client = _client()
client = _client(log_cb=_logger(sess))
app_token_resp = await client.get_app_token(scope="user:create")
app_token = app_token_resp.get("access_token", "")
@@ -460,7 +458,6 @@ async def step6(request: Request):
except Exception as e:
err_str = str(e)
if "404" in err_str:
# Sandbox doesn't expose webhook management — show sample data instead
result_webhooks = {"note": "Webhook API ikke tilgængeligt i sandbox — kun i produktion"}
webhook_registered = {
"url": webhook_url,
@@ -470,8 +467,7 @@ async def step6(request: Request):
else:
error = err_str
return templates.TemplateResponse("step6.html", {
"request": request,
return templates.TemplateResponse("step6.html", _ctx(request, {
"step": 6,
"title": "Webhooks & Events",
"subtitle": "Real-time Event Notifications",
@@ -485,7 +481,28 @@ async def step6(request: Request):
"app_base_url": s.app_base_url,
"next_step": None,
"prev_step": 5,
})
}))
# ---------------------------------------------------------------------------
# API Request Log
# ---------------------------------------------------------------------------
@router.get("/demo/log", response_class=HTMLResponse)
async def api_log(request: Request):
sess = _session(request)
log = sess.get("api_log", [])
return templates.TemplateResponse("log.html", _ctx(request, {
"log": list(reversed(log)), # newest first
"log_count": len(log),
}))
@router.post("/demo/log/clear")
async def clear_log(request: Request):
sess = _session(request)
sess["api_log"] = []
return RedirectResponse("/demo/log", status_code=303)
# ---------------------------------------------------------------------------