Skip to content

Commit 51eaa4b

Browse files
jopemachineclaude
andcommitted
fix(BA-6921): read scope_id as the owner kind its scope_type names
The request body carries scope_id as a plain UUID, but the creator spec now takes an AppConfigScopeIdentifier — only scope_type says whether that UUID is a domain or a user. The adapter is the boundary that knows both, so it does the reading. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 052dfec commit 51eaa4b

1 file changed

Lines changed: 27 additions & 4 deletions

File tree

  • src/ai/backend/manager/api/adapters/app_config_fragment

src/ai/backend/manager/api/adapters/app_config_fragment/adapter.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
from uuid import UUID
6+
57
from ai.backend.common.data.app_config.types import AppConfigScopeType
68
from ai.backend.common.data.app_config.types import AppConfigScopeType as AppConfigScopeTypeDTO
79
from ai.backend.common.dto.manager.v2.app_config_fragment.request import (
@@ -19,11 +21,15 @@
1921
PurgeAppConfigFragmentPayload,
2022
UpdateAppConfigFragmentPayload,
2123
)
24+
from ai.backend.common.identifier.app_config import AppConfigScopeIdentifier
2225
from ai.backend.common.identifier.app_config_fragment import AppConfigFragmentID
26+
from ai.backend.common.identifier.domain import DomainID
27+
from ai.backend.common.identifier.user import UserID
2328
from ai.backend.manager.api.adapters.base import BaseAdapter
2429
from ai.backend.manager.data.app_config_fragment.types import (
2530
AppConfigFragmentData,
2631
)
32+
from ai.backend.manager.errors.api import InvalidAPIParameters
2733
from ai.backend.manager.repositories.app_config_fragment.creators import (
2834
AppConfigFragmentCreatorSpec,
2935
)
@@ -57,18 +63,35 @@
5763
from ai.backend.manager.types import OptionalState
5864

5965

66+
def _scope_owner(scope_type: AppConfigScopeType, scope_id: UUID | None) -> AppConfigScopeIdentifier:
67+
"""Read a request's raw ``scope_id`` as the kind of owner its ``scope_type`` calls for.
68+
69+
A request body carries ``scope_id`` as a plain UUID; only ``scope_type`` says whether it
70+
names a domain or a user. The DTO validator already rejects the mismatched combinations,
71+
so the final branch is a boundary guard rather than a reachable path.
72+
"""
73+
match scope_type, scope_id:
74+
case AppConfigScopeType.PUBLIC, None:
75+
return None
76+
case AppConfigScopeType.DOMAIN, UUID() as owner:
77+
return DomainID(owner)
78+
case AppConfigScopeType.USER, UUID() as owner:
79+
return UserID(owner)
80+
case _:
81+
raise InvalidAPIParameters(f"scope_id does not match the {scope_type.value} scope.")
82+
83+
6084
class AppConfigFragmentAdapter(BaseAdapter):
6185
"""Adapter for raw app config fragment write operations."""
6286

6387
# --- fragment CRUD (RBAC-gated at the processor) ---
6488

6589
async def create(self, input: CreateAppConfigFragmentInput) -> CreateAppConfigFragmentPayload:
66-
# scope_id is None exactly for public (enforced by the DTO validator), which is what
67-
# the column stores for an ownerless fragment.
90+
scope_type = AppConfigScopeType(input.scope_type.value)
6891
spec = AppConfigFragmentCreatorSpec(
6992
config_name=input.config_name,
70-
scope_type=AppConfigScopeType(input.scope_type.value),
71-
scope_id=input.scope_id,
93+
scope_type=scope_type,
94+
scope_id=_scope_owner(scope_type, input.scope_id),
7295
config=input.config,
7396
)
7497
action_result = await self._processors.app_config_fragment.create.wait_for_complete(

0 commit comments

Comments
 (0)