fix(M02): 층 길 저장 PUT 이 뼈대 없는 문서(빈 {} · 표 열 목록 없음 · 도면 entities 없음)를 400 으로 거절 — 파일 안 씀 (브레인 급한 흠)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015LuapLYqN1GGFD8Y1PStD5
This commit is contained in:
2026-09-25 12:43:25 +09:00
co-authored by Claude Sonnet 5
parent dd60b6a1cb
commit 28b7e70f28
3 changed files with 36 additions and 5 deletions
@@ -253,6 +253,10 @@ async def save_layer_template(
) -> dict[str, Any]:
folder = await _layer_dir(session, layer, project_id=project_id, write=True)
document = body.문서
try:
layers.check_skeleton(kind, document) # 빈 `{}` · 뼈대 없는 문서는 파일 안 씀
except ValueError as error:
raise _bad(error) from error
if kind == "table" and fill.is_fillable(document):
# 채운 표가 와도 설계값은 안 받아 적음 — 양식 + 손 값만(5장 · 브라우저 값을 믿지 않음)
document = fill.strip_design(document)
@@ -159,6 +159,15 @@ def read_template(layer_dir: str | Path, kind: str, name: str) -> dict[str, Any]
}
def check_skeleton(kind: str, document: Any) -> None:
"""뼈대 없는 문서는 거절 — 표 = `열` 목록 · 도면 = `entities` 목록(빈 `{}` 저장 막기)."""
key = {"table": "열", "drawing": "entities"}.get(kind)
if not isinstance(document, dict):
raise ValueError("양식 문서는 JSON 객체여야 합니다.")
if key and not isinstance(document.get(key), list):
raise ValueError(f"양식 문서에 `{key}` 목록이 없습니다 — 빈 문서는 저장하지 않습니다.")
def write_template(
layer_dir: str | Path,
kind: str,
+23 -5
View File
@@ -102,9 +102,9 @@ def test_작업본_저장_판_다르면_409_초기화는_초기본으로(world:
client = world["client"]
got = client.get(_url()).json()
assert got["층"] == "project" and got["문서"]["판"] == 1
saved = client.put(_url(), json={"판": got["판"], "문서": {"판": 2}})
saved = client.put(_url(), json={"판": got["판"], "문서": {"판": 2, "열": []}})
assert saved.status_code == 200
stale = client.put(_url(), json={"판": got["판"], "문서": {"판": 3}})
stale = client.put(_url(), json={"판": got["판"], "문서": {"판": 3, "열": []}})
assert stale.status_code == 409
reset = client.post(f"/api/m02/projects/{P1}/templates/reset", json={})
assert "table/구조물집계표" in reset.json()["초기화"]
@@ -115,7 +115,7 @@ def test_작업본_저장_판_다르면_409_초기화는_초기본으로(world:
def test_회사_공식_저장은_관리자만_적용은_작업본만(world: dict[str, Any]) -> None:
client, session = world["client"], world["session"]
client.put(_url(), json={"판": client.get(_url()).json()["판"], "문서": {"회사": 1}})
client.put(_url(), json={"판": client.get(_url()).json()["판"], "문서": {"회사": 1, "열": []}})
body = {"to": "company", "종류": "table", "이름": "구조물집계표"}
assert client.post(f"/api/m02/projects/{P1}/templates/save-as", json=body).status_code == 403
session["role"] = "ADMIN"
@@ -123,7 +123,10 @@ def test_회사_공식_저장은_관리자만_적용은_작업본만(world: dict
applied = client.post(f"/api/m02/projects/{P2}/templates/apply", json={"from": "company"})
assert applied.status_code == 200
root2 = _root(world, P2, user=43)
assert layers.read_template(root2 / "templates", "table", "구조물집계표")["문서"] == {"회사": 1}
assert layers.read_template(root2 / "templates", "table", "구조물집계표")["문서"] == {
"회사": 1,
"열": [],
}
assert (
layers.read_template(root2 / "templates/_initial", "table", "구조물집계표")["문서"]["판"]
== 1
@@ -142,7 +145,7 @@ def test_개인_양식은_본인만_쓰고_같은_회사는_읽어_가져옴(wor
assert client.get(theirs).status_code == 200
put = client.put(
f"/api/m02/layers/personal/templates/table/구조물집계표?project_id={P1}&user_id=42",
json={"판": None, "문서": {}},
json={"판": None, "문서": {"열": []}},
)
assert put.status_code == 200 # user_id 는 쓰기에서 무시 — 본인(43) 자리에 새로 씀
assert (world["storage"] / "7/43/templates/table/구조물집계표.json").is_file()
@@ -336,3 +339,18 @@ def test_옛_표_틀은_새_판으로_손_값은_지킴(world: dict[str, Any]) -
# 손댄 것 없는 작업본은 시스템 파일 그대로(판이 같음)
migrate.refresh_tables(root2)
assert layers.version_of(root2 / "templates/table/구조물집계표.json") == system_version
def test_뼈대_없는_문서는_400으로_거절하고_파일을_안_씀(world: dict[str, Any]) -> None:
client = world["client"]
path = _root(world, P1) / "templates/drawing/A1_도각.json"
before = path.read_bytes()
url = f"/api/m02/layers/project/templates/drawing/A1_도각?project_id={P1}"
version = layers.version_of(path)
for document in ({}, {"format": 6}, {"entities": {}}):
got = client.put(url, json={"판": version, "문서": document})
assert got.status_code == 400 and "entities" in got.json()["detail"]
assert path.read_bytes() == before
table = _url()
assert client.put(table, json={"판": None, "문서": {}}).status_code == 400
assert client.put(url, json={"판": version, "문서": {"entities": []}}).status_code == 200