From 3e9c3b68bd1d748eabe02a62dbb58928a535e709 Mon Sep 17 00:00:00 2001 From: Donkie Date: Sun, 11 Oct 2026 10:33:19 +0200 Subject: [PATCH] Allow renaming a location whose name contains a slash PATCH /location/{location} returned 404 for names like "Shelf 1/2", even URL-encoded, because the path is decoded before routing and the default converter stops at a slash. Use the path converter. --- spoolman/api/v1/other.py | 2 +- .../tests/spool/test_rename_field_value.py | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/spoolman/api/v1/other.py b/spoolman/api/v1/other.py index 866aced9..c484d061 100644 --- a/spoolman/api/v1/other.py +++ b/spoolman/api/v1/other.py @@ -131,7 +131,7 @@ class RenameLocationBody(BaseModel): @router.patch( - "/location/{location}", + "/location/{location:path}", name="Rename location", description="Rename a spool location. All spools in this location will be moved to the new location.", response_model_exclude_none=True, diff --git a/tests_integration/tests/spool/test_rename_field_value.py b/tests_integration/tests/spool/test_rename_field_value.py index 02853c2b..506918ca 100644 --- a/tests_integration/tests/spool/test_rename_field_value.py +++ b/tests_integration/tests/spool/test_rename_field_value.py @@ -5,6 +5,7 @@ import uuid from collections.abc import Iterable from dataclasses import dataclass from typing import Any +from urllib.parse import quote import httpx import pytest @@ -177,3 +178,29 @@ def test_rename_location_rejects_too_long_a_value(): """The location column is 64 characters, so an over-long value is a 400 and not a crash.""" result = httpx.patch(rename_url("location"), json={"value": "Bottom", "new_value": "x" * 65}) assert result.status_code == 400 + + +def test_rename_location_with_slash(random_filament: dict[str, Any]): + """Renaming a slash-containing location moves every spool to the new location.""" + old_location = "Shelf 1/2" + new_location = "Shelf 3/4" + spool_ids = [] + for location in (old_location, old_location, "Other shelf"): + result = httpx.post( + f"{URL}/api/v1/spool", + json={"filament_id": random_filament["id"], "location": location}, + ) + result.raise_for_status() + spool_ids.append(result.json()["id"]) + + try: + result = httpx.patch( + f"{URL}/api/v1/location/{quote(old_location, safe='')}", + json={"name": new_location}, + ) + result.raise_for_status() + assert result.json() == new_location + assert [_spool(spool_id)["location"] for spool_id in spool_ids] == [new_location, new_location, "Other shelf"] + finally: + for spool_id in spool_ids: + httpx.delete(f"{URL}/api/v1/spool/{spool_id}").raise_for_status()