From 6c0210ebc5cd426c4ea06bb3072c316dd51db104 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 30 Sep 2026 13:20:19 +0900 Subject: [PATCH] =?UTF-8?q?feat(M02):=20=EC=A1=B0=ED=95=A9=20=ED=82=A4=20?= =?UTF-8?q?=ED=95=98=EB=82=98=20=EC=9D=BD=EA=B8=B0=20/api/m02/combos/key/{?= =?UTF-8?q?=ED=82=A4}=20=E2=80=94=20=EC=96=B4=EB=8A=90=20=EC=B8=B5?= =?UTF-8?q?=EC=9D=B4=EB=93=A0=20=C2=B7=20=EA=B7=B8=20=EC=B8=B5=20=EC=9D=BD?= =?UTF-8?q?=EA=B8=B0=20=EA=B6=8C=ED=95=9C(PLAN=2027-2c=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BU9gbNE3aWFUvE61pS2WGt --- M02_MasterTemplete/M02_MasterTemplete_Combo_Store.py | 11 +++++++++++ .../M02_MasterTemplete_Router_Combos.py | 10 ++++++++++ resources/tester/test_m02_combo_layers.py | 7 +++++++ 3 files changed, 28 insertions(+) diff --git a/M02_MasterTemplete/M02_MasterTemplete_Combo_Store.py b/M02_MasterTemplete/M02_MasterTemplete_Combo_Store.py index d328a891..acd75edf 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Combo_Store.py +++ b/M02_MasterTemplete/M02_MasterTemplete_Combo_Store.py @@ -84,6 +84,17 @@ def find(key: str) -> dict[str, Any] | None: return next((r for r in _rows_at(_file(key)) if str(r.get("키")) == key), None) +def where(key: str) -> tuple[str, str | None] | None: + """키의 층 · 주인 — ("system", None) · ("company", "7") · ("personal", "7/42") · 모르면 None.""" + key = str(key or "") + got, path = _HEAD.match(key), _file(key) + if key.startswith(MASTER) or path is None or not got: + return ("system", None) if key.startswith(MASTER) else None + if got[1] == "C": + return ("company", got[2]) + return ("personal", f"{path.parent.parent.parent.name}/{got[2]}") # {회사}/{사용자}/templates + + def names() -> dict[str, str]: """지금 층이 쓸 수 있는 조합 키 → 이름(마스터 · 회사 · 개인).""" out: dict[str, str] = {} diff --git a/M02_MasterTemplete/M02_MasterTemplete_Router_Combos.py b/M02_MasterTemplete/M02_MasterTemplete_Router_Combos.py index fb4ea592..078e58ba 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Router_Combos.py +++ b/M02_MasterTemplete/M02_MasterTemplete_Router_Combos.py @@ -67,6 +67,16 @@ def get_all(session: Session = Depends(verify_session)) -> list[dict]: return out +@router.get("/combos/key/{key}") +def get_by_key(key: str, session: Session = Depends(verify_session)) -> dict: + """키 하나(어느 층이든 · 그 층 읽기 권한) — `{layer, owner, 판, combo, 줄, …}`.""" + got = combos.where(key) + if got is None: + raise HTTPException(status_code=404, detail=f"없는 조합 「{key}」") + layer, owner = got + return {"layer": layer, "owner": owner, **_call(_root(session, layer, owner), combos.one, key)} + + @router.get("/layers/{layer}/combos") def get_combos(layer: Layer, owner: str | None = None, session: Session = Depends(verify_session)): return _call(_root(session, layer, owner), combos.listing) diff --git a/resources/tester/test_m02_combo_layers.py b/resources/tester/test_m02_combo_layers.py index e16f7a39..6a71f801 100644 --- a/resources/tester/test_m02_combo_layers.py +++ b/resources/tester/test_m02_combo_layers.py @@ -223,6 +223,13 @@ def test_세_층_목록_고르기_남의_층_403_갈라_낸_것은_그_층에(wo assert [r["키"] for r in all_[1]["줄"]] == [uc] and [r["키"] for r in all_[2]["줄"]] == [up] assert combos.find(uc)["이름"] == "회사 조합" and combos.find(up)["이름"] == "내 조합" assert combos.find("UP42-999999") is None and combos.find("엉터리") is None + by_key = me.get(f"/api/m02/combos/key/{up}").json() + assert (by_key["layer"], by_key["owner"], by_key["combo"]["키"]) == ("personal", "7/42", up) + assert me.get(f"/api/m02/combos/key/{uc}").json()["owner"] == "7" + assert me.get("/api/m02/combos/key/UP42-999999").status_code == 404 + world["session"]["company_id"] = 8 # 다른 회사 사람 — 남의 회사 조합은 못 봄 + assert me.get(f"/api/m02/combos/key/{uc}").status_code == 403 + world["session"]["company_id"] = 7 column = me.post( "/api/m02/layers/personal/tree",