mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 04:38:55 +00:00
fix: enforce wall model barrier in backend optimize
Issue: #282 User-Visible: yes
This commit is contained in:
@@ -60,6 +60,11 @@ from .virtual_lights import (
|
||||
)
|
||||
from .registry_snapshot import import_registry_snapshot
|
||||
from .projection import project_config, project_layout
|
||||
from .wall_segment_model import (
|
||||
WALL_SEGMENT_MODEL_VERSION,
|
||||
WallSegmentMigrationError,
|
||||
commit_wall_segment_model,
|
||||
)
|
||||
from .validation import (
|
||||
CONFIG_SCHEMA, LAYOUT_SCHEMA, MAX_CONFIG_BYTES, MAX_PLAN_BYTES,
|
||||
PLAN_EXTENSIONS, POS_SCHEMA, MarkerControlError, OpeningPassageError,
|
||||
@@ -1633,7 +1638,26 @@ async def ws_plan_optimize(hass: HomeAssistant, connection, msg: dict[str, Any])
|
||||
# as config/set; otherwise a crafted client can persist a new cycle.
|
||||
try:
|
||||
validate_wall_model_transition(msg["config"], config_data.get("config"))
|
||||
validated_config = CONFIG_SCHEMA(msg["config"])
|
||||
try:
|
||||
submitted_model = int(msg["config"].get("model_version", 0) or 0)
|
||||
except (TypeError, ValueError):
|
||||
submitted_model = WALL_SEGMENT_MODEL_VERSION
|
||||
# Optimize is an explicit structural writer and therefore the
|
||||
# server-side v7 -> v8 barrier as well. Current v8 candidates are
|
||||
# validated verbatim: the backend must never invent lineage for a
|
||||
# graph already authored by a current client.
|
||||
candidate_config = CONFIG_SCHEMA(msg["config"])
|
||||
if submitted_model < WALL_SEGMENT_MODEL_VERSION:
|
||||
candidate_config, _ = commit_wall_segment_model(candidate_config)
|
||||
validated_config = CONFIG_SCHEMA(candidate_config)
|
||||
migrated_size = len(json.dumps(validated_config, separators=(",", ":")))
|
||||
if migrated_size > MAX_CONFIG_BYTES:
|
||||
connection.send_error(
|
||||
msg["id"], "too_large",
|
||||
f"Configuration is {migrated_size // 1024} KB, "
|
||||
f"the limit is {MAX_CONFIG_BYTES // 1024} KB",
|
||||
)
|
||||
return
|
||||
msg["config"].clear()
|
||||
msg["config"].update(validated_config)
|
||||
validate_marker_controls(msg["config"], config_data.get("config"))
|
||||
@@ -1647,6 +1671,7 @@ async def ws_plan_optimize(hass: HomeAssistant, connection, msg: dict[str, Any])
|
||||
except (
|
||||
MarkerControlError, OpeningPassageError, PartitionOpeningHostError,
|
||||
PartitionOpeningJambMarginError, WallModelClientOutdatedError,
|
||||
WallSegmentMigrationError,
|
||||
) as err:
|
||||
connection.send_error(msg["id"], err.code, str(err))
|
||||
return
|
||||
|
||||
+3
-2
@@ -18,8 +18,9 @@
|
||||
passage ownership attached to stable wall-segment IDs. Existing plans upgrade
|
||||
atomically on their first structural edit or through **Optimize plans**;
|
||||
Resize, Undo/Redo and repeated edits preserve the same identities without
|
||||
accumulating off-grid coordinates. Ambiguous legacy geometry is left
|
||||
unchanged with a recovery prompt. No Plan editor controls or
|
||||
accumulating off-grid coordinates. The backend Optimize transaction also
|
||||
enforces this upgrade when invoked directly. Ambiguous legacy geometry is
|
||||
left unchanged with a recovery prompt. No Plan editor controls or
|
||||
zero-thickness-wall behaviour changed
|
||||
([#282](https://github.com/Matysh/houseplan-card/issues/282)).
|
||||
- The active wall chain keeps its axis and node markers visible on already
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
сегментов и принадлежность дверей, окон, ворот и проходов через стабильные ID
|
||||
сегментов стен. Существующий план обновляется атомарно при первом структурном
|
||||
изменении или через **«Оптимизировать планы»**; Resize, отмена/повтор и
|
||||
последующие правки сохраняют те же ID без накопления координат вне сетки.
|
||||
последующие правки сохраняют те же ID без накопления координат вне сетки;
|
||||
серверная транзакция Optimize также гарантирует это обновление при прямом
|
||||
вызове.
|
||||
Неоднозначная старая геометрия остаётся без изменений с подсказкой по
|
||||
восстановлению. Кнопки редактора плана и поведение стен нулевой толщины не
|
||||
менялись
|
||||
|
||||
@@ -568,8 +568,13 @@ async def test_optimize_accepts_proved_rehost_and_undo_restores_host(
|
||||
optimized = await client.receive_json()
|
||||
assert optimized["success"]
|
||||
stored = (await runtime.config_store.async_load())["config"]
|
||||
assert stored["spaces"][0].get("partitions") in (None, [])
|
||||
assert "host" not in stored["spaces"][0]["openings"][0]
|
||||
stored_space = stored["spaces"][0]
|
||||
assert stored_space.get("partitions") in (None, [])
|
||||
wall_ids = {segment["id"] for segment in stored_space["wall_segments"]}
|
||||
assert wall_ids
|
||||
for opening in stored_space["openings"]:
|
||||
assert opening["host"]["kind"] == "wall"
|
||||
assert opening["host"]["id"] in wall_ids
|
||||
|
||||
await client.send_json_auto_id({
|
||||
"type": "houseplan/plan/optimize_undo",
|
||||
|
||||
Reference in New Issue
Block a user