mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-31 10:12:21 +01:00
Merge pull request #6585 from robyroro/codex/notes-clear-fields
fix(notes): persist removal of optional note fields
This commit is contained in:
@@ -728,32 +728,35 @@ def setup_note_routes(task_scheduler=None, upload_handler=None):
|
|||||||
)
|
)
|
||||||
if body.title is not None:
|
if body.title is not None:
|
||||||
note.title = body.title
|
note.title = body.title
|
||||||
if body.content is not None:
|
# Omitted fields preserve their values; explicit null clears the
|
||||||
|
# nullable fields used by the editor's removal controls.
|
||||||
|
supplied = body.model_fields_set
|
||||||
|
if "content" in supplied:
|
||||||
note.content = body.content
|
note.content = body.content
|
||||||
if body.items is not None:
|
if "items" in supplied:
|
||||||
note.items = json.dumps(body.items)
|
note.items = json.dumps(body.items) if body.items is not None else None
|
||||||
flag_modified(note, "items")
|
flag_modified(note, "items")
|
||||||
if body.note_type is not None:
|
if body.note_type is not None:
|
||||||
note.note_type = body.note_type
|
note.note_type = body.note_type
|
||||||
if body.color is not None:
|
if "color" in supplied:
|
||||||
note.color = body.color
|
note.color = body.color
|
||||||
if body.label is not None:
|
if "label" in supplied:
|
||||||
note.label = body.label
|
note.label = body.label
|
||||||
if body.pinned is not None:
|
if body.pinned is not None:
|
||||||
note.pinned = body.pinned
|
note.pinned = body.pinned
|
||||||
if body.archived is not None:
|
if body.archived is not None:
|
||||||
note.archived = body.archived
|
note.archived = body.archived
|
||||||
if body.due_date is not None:
|
if "due_date" in supplied:
|
||||||
note.due_date = body.due_date
|
note.due_date = body.due_date
|
||||||
if body.image_url is not None:
|
if "image_url" in supplied:
|
||||||
note.image_url = body.image_url
|
note.image_url = body.image_url
|
||||||
if body.gallery_id is not None:
|
if "gallery_id" in supplied:
|
||||||
note.gallery_id = body.gallery_id
|
note.gallery_id = body.gallery_id
|
||||||
if body.repeat is not None:
|
if body.repeat is not None:
|
||||||
note.repeat = body.repeat
|
note.repeat = body.repeat
|
||||||
if body.sort_order is not None:
|
if body.sort_order is not None:
|
||||||
note.sort_order = body.sort_order
|
note.sort_order = body.sort_order
|
||||||
if body.agent_session_id is not None:
|
if "agent_session_id" in supplied:
|
||||||
note.agent_session_id = body.agent_session_id
|
note.agent_session_id = body.agent_session_id
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
"""Explicit null must clear nullable note fields; omission must preserve them."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from tests.helpers.database import disposable_database
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def client(tmp_path, monkeypatch):
|
||||||
|
import routes.note.note_routes as routes
|
||||||
|
|
||||||
|
with disposable_database(tmp_path) as factory:
|
||||||
|
monkeypatch.setattr(routes, "SessionLocal", factory)
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
@app.middleware("http")
|
||||||
|
async def identity(request, call_next):
|
||||||
|
request.state.current_user = request.headers.get("x-test-user", "alice")
|
||||||
|
return await call_next(request)
|
||||||
|
|
||||||
|
monkeypatch.setattr(routes, "_scheduler_ref", None)
|
||||||
|
app.include_router(routes.setup_note_routes())
|
||||||
|
with TestClient(app) as api:
|
||||||
|
yield api
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("field,value", [
|
||||||
|
("due_date", "2030-10-08T09:00:00"),
|
||||||
|
("label", "home"),
|
||||||
|
("image_url", "https://example.com/note.png"),
|
||||||
|
("content", "Saved body"),
|
||||||
|
("items", [{"text": "Saved item", "done": False}]),
|
||||||
|
("color", "blue"),
|
||||||
|
("gallery_id", "gallery-1"),
|
||||||
|
("agent_session_id", "chat-1"),
|
||||||
|
])
|
||||||
|
def test_explicit_null_clears_saved_field(client, field, value):
|
||||||
|
created = client.post("/api/notes", json={"title": "Test note"})
|
||||||
|
assert created.status_code == 200, created.text
|
||||||
|
path = f"/api/notes/{created.json()['id']}"
|
||||||
|
populated = client.put(path, json={field: value})
|
||||||
|
assert populated.status_code == 200, populated.text
|
||||||
|
assert client.get(path).json()[field] == value
|
||||||
|
|
||||||
|
updated = client.put(path, json={field: None})
|
||||||
|
assert updated.status_code == 200, updated.text
|
||||||
|
saved = client.get(path)
|
||||||
|
assert saved.status_code == 200, saved.text
|
||||||
|
assert saved.json()[field] is None
|
||||||
|
assert updated.json()[field] is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_omitted_fields_survive_title_edit(client):
|
||||||
|
fields = {
|
||||||
|
"due_date": "2030-10-08T09:00:00",
|
||||||
|
"label": "home",
|
||||||
|
"image_url": "https://example.com/note.png",
|
||||||
|
"content": "Saved body",
|
||||||
|
"items": [{"text": "Saved item", "done": False}],
|
||||||
|
"color": "blue",
|
||||||
|
"gallery_id": "gallery-1",
|
||||||
|
}
|
||||||
|
created = client.post("/api/notes", json={"title": "Before", **fields})
|
||||||
|
assert created.status_code == 200, created.text
|
||||||
|
path = f"/api/notes/{created.json()['id']}"
|
||||||
|
updated = client.put(path, json={"title": "After"})
|
||||||
|
assert updated.status_code == 200, updated.text
|
||||||
|
saved = client.get(path).json()
|
||||||
|
assert saved["title"] == "After"
|
||||||
|
for field, value in fields.items():
|
||||||
|
assert saved[field] == value
|
||||||
|
|
||||||
|
|
||||||
|
def test_null_update_cannot_clear_another_users_note(client):
|
||||||
|
created = client.post("/api/notes", json={"title": "Private", "label": "home"})
|
||||||
|
assert created.status_code == 200, created.text
|
||||||
|
path = f"/api/notes/{created.json()['id']}"
|
||||||
|
response = client.put(path, json={"label": None}, headers={"x-test-user": "bob"})
|
||||||
|
assert response.status_code == 404
|
||||||
|
assert client.get(path).json()["label"] == "home"
|
||||||
Reference in New Issue
Block a user