feat: add GET/PUT /api/v1/settings endpoints
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V57jSQPqwkGG8BuAXg59X5
This commit is contained in:
parent
df933d089b
commit
a904f2f8e0
|
|
@ -3,7 +3,7 @@ JARVIS API Backend
|
|||
FastAPI application for JARVIS AI Assistant + Business Automation
|
||||
"""
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Depends, Header
|
||||
from fastapi import FastAPI, HTTPException, Depends, Header, Body
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
|
@ -1497,6 +1497,20 @@ async def get_orders(status: Optional[str] = None):
|
|||
logger.error(f"Orders fetch error: {str(e)}")
|
||||
raise HTTPException(status_code=503, detail=f"Orders unavailable: {str(e)}")
|
||||
|
||||
@app.get("/api/v1/settings", dependencies=[Depends(require_admin_key)])
|
||||
async def get_settings_endpoint():
|
||||
return await get_all_settings()
|
||||
|
||||
|
||||
@app.put("/api/v1/settings", dependencies=[Depends(require_admin_key)])
|
||||
async def update_settings_endpoint(updates: dict = Body(...)):
|
||||
for key in updates:
|
||||
if key not in SETTINGS_DEFAULTS:
|
||||
raise HTTPException(status_code=400, detail=f"Unknown setting: {key}")
|
||||
for key, value in updates.items():
|
||||
await set_setting(key, str(value))
|
||||
return await get_all_settings()
|
||||
|
||||
@app.get("/api/v1/emails", dependencies=[Depends(require_admin_key)])
|
||||
async def get_cached_emails(limit: int = 10):
|
||||
"""Recently cached emails, populated by the n8n 'Neue E-Mails Cache' workflow"""
|
||||
|
|
|
|||
|
|
@ -62,3 +62,54 @@ async def test_set_setting_upserts_value():
|
|||
assert "INSERT INTO settings" in args[0]
|
||||
assert "ON CONFLICT (key) DO UPDATE" in args[0]
|
||||
assert args[1] == ("company_name", "Neue Firma GmbH")
|
||||
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
main.API_KEY_ADMIN = "test-secret"
|
||||
HEADERS = {"X-Admin-Key": "test-secret"}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client():
|
||||
with TestClient(main.app) as c:
|
||||
yield c
|
||||
|
||||
|
||||
def test_get_settings_endpoint_returns_merged_settings(client):
|
||||
settings = {"assistant_name": "JARVIS", "company_name": "MBO-Tech-IT", "contact_email": "kontakt@mbo-tech-it.de"}
|
||||
with patch.object(main, "get_all_settings", new=AsyncMock(return_value=settings)):
|
||||
response = client.get("/api/v1/settings", headers=HEADERS)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == settings
|
||||
|
||||
|
||||
def test_get_settings_endpoint_requires_admin_key(client):
|
||||
response = client.get("/api/v1/settings")
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_put_settings_endpoint_updates_and_returns_merged_settings(client):
|
||||
updated = {"assistant_name": "FRIDAY", "company_name": "MBO-Tech-IT", "contact_email": "kontakt@mbo-tech-it.de"}
|
||||
with patch.object(main, "set_setting", new=AsyncMock()) as mock_set, patch.object(
|
||||
main, "get_all_settings", new=AsyncMock(return_value=updated)
|
||||
):
|
||||
response = client.put("/api/v1/settings", headers=HEADERS, json={"assistant_name": "FRIDAY"})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == updated
|
||||
mock_set.assert_called_once_with("assistant_name", "FRIDAY")
|
||||
|
||||
|
||||
def test_put_settings_endpoint_rejects_unknown_key(client):
|
||||
with patch.object(main, "set_setting", new=AsyncMock()) as mock_set:
|
||||
response = client.put("/api/v1/settings", headers=HEADERS, json={"nonsense_key": "x"})
|
||||
|
||||
assert response.status_code == 400
|
||||
mock_set.assert_not_called()
|
||||
|
||||
|
||||
def test_put_settings_endpoint_requires_admin_key(client):
|
||||
response = client.put("/api/v1/settings", json={"assistant_name": "X"})
|
||||
assert response.status_code == 401
|
||||
|
|
|
|||
Loading…
Reference in New Issue