|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
| 5 | +from uuid import UUID |
| 6 | + |
5 | 7 | from ai.backend.common.data.app_config.types import AppConfigScopeType |
6 | 8 | from ai.backend.common.data.app_config.types import AppConfigScopeType as AppConfigScopeTypeDTO |
7 | 9 | from ai.backend.common.dto.manager.v2.app_config_fragment.request import ( |
|
19 | 21 | PurgeAppConfigFragmentPayload, |
20 | 22 | UpdateAppConfigFragmentPayload, |
21 | 23 | ) |
| 24 | +from ai.backend.common.identifier.app_config import AppConfigScopeIdentifier |
22 | 25 | 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 |
23 | 28 | from ai.backend.manager.api.adapters.base import BaseAdapter |
24 | 29 | from ai.backend.manager.data.app_config_fragment.types import ( |
25 | 30 | AppConfigFragmentData, |
26 | 31 | ) |
| 32 | +from ai.backend.manager.errors.api import InvalidAPIParameters |
27 | 33 | from ai.backend.manager.repositories.app_config_fragment.creators import ( |
28 | 34 | AppConfigFragmentCreatorSpec, |
29 | 35 | ) |
|
57 | 63 | from ai.backend.manager.types import OptionalState |
58 | 64 |
|
59 | 65 |
|
| 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 | + |
60 | 84 | class AppConfigFragmentAdapter(BaseAdapter): |
61 | 85 | """Adapter for raw app config fragment write operations.""" |
62 | 86 |
|
63 | 87 | # --- fragment CRUD (RBAC-gated at the processor) --- |
64 | 88 |
|
65 | 89 | 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) |
68 | 91 | spec = AppConfigFragmentCreatorSpec( |
69 | 92 | 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), |
72 | 95 | config=input.config, |
73 | 96 | ) |
74 | 97 | action_result = await self._processors.app_config_fragment.create.wait_for_complete( |
|
0 commit comments