fix(dashboard): 54-44c 화면 취향 저장 때 지워진 프로젝트 id 키 버림 — 브라우저 옛 사본의 재오염 막음 · 시험 1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UoSr2sqvYtBf1dtQdus88s
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
@@ -547,10 +548,23 @@ async def get_user_ui_prefs(user_id: int) -> dict[str, Any]:
|
||||
return raw if isinstance(raw, dict) else {}
|
||||
|
||||
|
||||
_UUID_IN_KEY = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
|
||||
|
||||
|
||||
async def save_user_ui_prefs(user_id: int, prefs: dict[str, Any]) -> None:
|
||||
"""화면 취향을 통째로 덮어쓴다 — 사용자당 한 줄."""
|
||||
"""화면 취향을 통째로 덮어쓴다 — 사용자당 한 줄.
|
||||
|
||||
지워진 프로젝트 id 가 박힌 키는 버림 — 브라우저 사본이 옛 키를 다시 올리지 못하게.
|
||||
"""
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor() as cursor:
|
||||
keyed = {key: m.group(0) for key in prefs if (m := _UUID_IN_KEY.search(key))}
|
||||
if keyed:
|
||||
ids = sorted(set(keyed.values()))
|
||||
holders = ", ".join(["%s"] * len(ids))
|
||||
await cursor.execute(f"SELECT id FROM projects WHERE id IN ({holders})", ids)
|
||||
live = {row[0] for row in await cursor.fetchall()}
|
||||
prefs = {k: v for k, v in prefs.items() if k not in keyed or keyed[k] in live}
|
||||
await cursor.execute(
|
||||
"""INSERT INTO user_ui_prefs (user_id, prefs) VALUES (%s, %s)
|
||||
ON DUPLICATE KEY UPDATE prefs = VALUES(prefs)""",
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
"""화면 취향 저장 — 지워진 프로젝트 id 가 박힌 키는 버림 · 나머지 키는 그대로."""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
import B01_Dashboard.B01_Dashboard_Repository as repo
|
||||
|
||||
LIVE = "226fc805-26c8-4f5a-9956-bacb200e10a5"
|
||||
DEAD = "936be972-11bc-46c2-8bf3-b15d8de7df0d"
|
||||
|
||||
|
||||
class _Cursor:
|
||||
def __init__(self, store):
|
||||
self.store = store
|
||||
self.rows = []
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *_):
|
||||
return False
|
||||
|
||||
async def execute(self, sql, args):
|
||||
if sql.startswith("SELECT id FROM projects"):
|
||||
self.rows = [(i,) for i in args if i == LIVE]
|
||||
else:
|
||||
self.store["saved"] = json.loads(args[1])
|
||||
|
||||
async def fetchall(self):
|
||||
return self.rows
|
||||
|
||||
|
||||
class _Conn:
|
||||
def __init__(self, store):
|
||||
self.store = store
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, *_):
|
||||
return False
|
||||
|
||||
def cursor(self):
|
||||
return _Cursor(self.store)
|
||||
|
||||
async def commit(self):
|
||||
pass
|
||||
|
||||
|
||||
class _Pool:
|
||||
def __init__(self, store):
|
||||
self.store = store
|
||||
|
||||
def acquire(self):
|
||||
return _Conn(self.store)
|
||||
|
||||
|
||||
def test_dead_project_keys_dropped(monkeypatch):
|
||||
store = {}
|
||||
monkeypatch.setattr(repo, "get_db_pool", lambda: _Pool(store))
|
||||
prefs = {
|
||||
f"aislo:pref:routeedit-view:{LIVE}": "a",
|
||||
f"aislo:pref:routeedit-view:{DEAD}": "b",
|
||||
"aislo:pref:table-open": "true",
|
||||
}
|
||||
asyncio.run(repo.save_user_ui_prefs(3, prefs))
|
||||
assert store["saved"] == {
|
||||
f"aislo:pref:routeedit-view:{LIVE}": "a",
|
||||
"aislo:pref:table-open": "true",
|
||||
}
|
||||
Reference in New Issue
Block a user