Skip to content

Commit 8cec165

Browse files
jopemachineclaude
andcommitted
refactor(BA-5829): split AppConfig merged-view into its own adapter; drop scoped GQL root
Per review on PR #11285: - Split the merged-view (AppConfig, BEP-1052 §5) surface and the self-service `my_bulk_*` writes out of `AppConfigFragmentAdapter` into a new `AppConfigAdapter`. Each adapter now handles a single domain DTO surface, matching the project convention. Both adapters share the same `app_config_fragment` service processors — splitting only the transport layer. - Drop the `scoped_app_config_fragments` GQL root resolver. BEP-1052 exposes the scope-bound list as child fields on `DomainV2.appConfigFragments` / `UserV2.appConfigFragments`, not as a root field. The scope-bound REST endpoint `POST /v2/app-config-fragments/{scope_type}/{scope_id}/search` continues to use `AppConfigFragmentAdapter.search()` directly. - Update the GQL resolvers (`my_app_configs`, `admin_app_configs`, `bulk_create_my_app_config_fragments`, `bulk_update_my_app_config_fragments`) to call the new `info.context.adapters.app_config` adapter. - Wire the new `app_config` field through `Adapters.__init__` and `Adapters.create()`. - DataLoader now uses `AppConfigFragmentFilter(id=UUIDFilter(...))` per the new filter shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6baca1d commit 8cec165

7 files changed

Lines changed: 55 additions & 40 deletions

File tree

src/ai/backend/manager/api/adapters/app_config.py

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""AppConfig (merged view) domain adapter
1+
"""AppConfig (merged view) domain adapter — BEP-1052 §5.
22
33
Reads the per-user merged AppConfig view and writes the underlying USER
44
fragments via the same `app_config_fragment` service processors. The
@@ -19,15 +19,15 @@
1919
)
2020
from ai.backend.common.dto.manager.v2.app_config.response import (
2121
AppConfigNode,
22+
BulkCreateMyAppConfigFragmentsPayload,
23+
BulkUpdateMyAppConfigFragmentsPayload,
2224
GetUserAppConfigPayload,
23-
MyBulkCreateAppConfigFragmentsPayload,
24-
MyBulkUpdateAppConfigFragmentsPayload,
2525
SearchAppConfigsPayload,
2626
)
2727
from ai.backend.common.dto.manager.v2.app_config.types import AppConfigOrderField, OrderDirection
2828
from ai.backend.common.dto.manager.v2.app_config_fragment.request import (
29-
MyBulkCreateAppConfigFragmentsInput,
30-
MyBulkUpdateAppConfigFragmentsInput,
29+
BulkCreateMyAppConfigFragmentsInput,
30+
BulkUpdateMyAppConfigFragmentsInput,
3131
)
3232
from ai.backend.common.dto.manager.v2.app_config_fragment.response import (
3333
AppConfigFragmentBulkError,
@@ -52,14 +52,14 @@
5252
from ai.backend.manager.services.app_config_fragment.actions.admin_search_app_configs import (
5353
AdminSearchAppConfigsAction,
5454
)
55-
from ai.backend.manager.services.app_config_fragment.actions.get_user_app_config import (
56-
GetUserAppConfigAction,
55+
from ai.backend.manager.services.app_config_fragment.actions.bulk_create_my import (
56+
BulkCreateMyAppConfigFragmentsAction,
5757
)
58-
from ai.backend.manager.services.app_config_fragment.actions.my_bulk_create import (
59-
MyBulkCreateAppConfigFragmentsAction,
58+
from ai.backend.manager.services.app_config_fragment.actions.bulk_update_my import (
59+
BulkUpdateMyAppConfigFragmentsAction,
6060
)
61-
from ai.backend.manager.services.app_config_fragment.actions.my_bulk_update import (
62-
MyBulkUpdateAppConfigFragmentsAction,
61+
from ai.backend.manager.services.app_config_fragment.actions.get_user_app_config import (
62+
GetUserAppConfigAction,
6363
)
6464
from ai.backend.manager.services.app_config_fragment.actions.search_user_app_configs import (
6565
SearchUserAppConfigsAction,
@@ -69,7 +69,7 @@
6969

7070

7171
class AppConfigAdapter(BaseAdapter):
72-
"""Adapter for the merged AppConfig view.
72+
"""Adapter for the merged AppConfig view (BEP-1052 §5).
7373
7474
Backed by the `app_config_fragment` service processors — the merged
7575
view is computed from raw fragments — but exposed as a separate
@@ -146,41 +146,55 @@ async def admin_search_app_configs(
146146
has_previous_page=result.has_previous_page,
147147
)
148148

149-
# ── Self-service bulk writes ───────────────────────
149+
# ── Self-service bulk writes (BEP-1052 §3) ───────────────────────
150150
#
151151
# Each bulk processor returns a `BulkProcessResult[T]` whose
152152
# `.result` field is the underlying `*ActionResult` produced by the
153153
# service. We discard the validator-decision trail here — RBAC
154154
# reasons travel back through the per-item `failed` list.
155155

156156
async def my_bulk_create(
157-
self, input: MyBulkCreateAppConfigFragmentsInput
158-
) -> MyBulkCreateAppConfigFragmentsPayload:
157+
self, input: BulkCreateMyAppConfigFragmentsInput
158+
) -> BulkCreateMyAppConfigFragmentsPayload:
159+
me = current_user()
160+
if me is None:
161+
raise UnreachableError("User context is not available")
159162
items = [
160163
MyAppConfigFragmentBulkItem(name=item.name, config=dict(item.config))
161164
for item in input.items
162165
]
163-
wrapper = await self._processors.app_config_fragment.my_bulk_create.wait_for_complete(
164-
MyBulkCreateAppConfigFragmentsAction(entity_ids=[], items=items)
166+
wrapper = await self._processors.app_config_fragment.bulk_create_my.wait_for_complete(
167+
BulkCreateMyAppConfigFragmentsAction(
168+
entity_ids=[],
169+
user_id=me.user_id,
170+
items=items,
171+
)
165172
)
166173
result = wrapper.result
167-
return MyBulkCreateAppConfigFragmentsPayload(
174+
return BulkCreateMyAppConfigFragmentsPayload(
168175
created=[self._data_to_dto(item) for item in result.created],
169176
failed=[self._bulk_error_to_dto(err) for err in result.failed],
170177
)
171178

172179
async def my_bulk_update(
173-
self, input: MyBulkUpdateAppConfigFragmentsInput
174-
) -> MyBulkUpdateAppConfigFragmentsPayload:
180+
self, input: BulkUpdateMyAppConfigFragmentsInput
181+
) -> BulkUpdateMyAppConfigFragmentsPayload:
182+
me = current_user()
183+
if me is None:
184+
raise UnreachableError("User context is not available")
175185
items = [
176186
MyAppConfigFragmentBulkItem(name=item.name, config=dict(item.config))
177187
for item in input.items
178188
]
179-
wrapper = await self._processors.app_config_fragment.my_bulk_update.wait_for_complete(
180-
MyBulkUpdateAppConfigFragmentsAction(entity_ids=[], items=items)
189+
wrapper = await self._processors.app_config_fragment.bulk_update_my.wait_for_complete(
190+
BulkUpdateMyAppConfigFragmentsAction(
191+
entity_ids=[],
192+
user_id=me.user_id,
193+
items=items,
194+
)
181195
)
182196
result = wrapper.result
183-
return MyBulkUpdateAppConfigFragmentsPayload(
197+
return BulkUpdateMyAppConfigFragmentsPayload(
184198
updated=[self._data_to_dto(item) for item in result.updated],
185199
failed=[self._bulk_error_to_dto(err) for err in result.failed],
186200
)

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from ai.backend.common.dto.manager.v2.app_config_fragment.types import (
5353
AppConfigScopeType as DTOAppConfigScopeType,
5454
)
55+
from ai.backend.common.dto.manager.v2.app_config_fragment.types import OrderDirection
5556
from ai.backend.manager.api.adapter_options.pagination.pagination import PaginationSpec
5657
from ai.backend.manager.data.app_config.types import AppConfigData
5758
from ai.backend.manager.data.app_config_fragment.bulk_types import (
@@ -108,9 +109,9 @@
108109

109110

110111
class AppConfigFragmentAdapter(BaseAdapter):
111-
"""Adapter for AppConfigFragment raw-row operations.
112+
"""Adapter for AppConfigFragment raw-row operations (BEP-1052 §2).
112113
113-
Writes are bulk-only; single-item create / update /
114+
Writes are bulk-only (BEP-1052 §3); single-item create / update /
114115
purge entry points are intentionally absent. Self-service my_bulk
115116
writes (which return the recomputed merged view) live on
116117
`AppConfigAdapter` alongside the merged-view reads.
@@ -265,7 +266,7 @@ def _data_to_dto(data: AppConfigFragmentData) -> AppConfigFragmentNode:
265266
updated_at=data.updated_at,
266267
)
267268

268-
# ── Bulk mutations ───────────────────────────────
269+
# ── Bulk mutations (BEP-1052 §3) ───────────────────────────────
269270
#
270271
# Each bulk processor returns a `BulkProcessResult[T]` whose
271272
# `.result` field is the underlying `*ActionResult` produced by the

src/ai/backend/manager/api/gql/app_config_fragment/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
admin_bulk_purge_app_config_fragments,
77
admin_bulk_update_app_config_fragments,
88
app_config_fragment,
9-
my_bulk_create_app_config_fragments,
10-
my_bulk_update_app_config_fragments,
9+
bulk_create_my_app_config_fragments,
10+
bulk_update_my_app_config_fragments,
1111
)
1212
from .types import (
1313
AppConfigFragmentFilterGQL,
@@ -19,7 +19,7 @@
1919
)
2020

2121
__all__ = [
22-
# Queries — scope-bound list belongs on DomainV2 / UserV2 child fields
22+
# Queries — scope-bound list belongs on DomainV2 / UserV2 child fields per BEP-1052
2323
"app_config_fragment",
2424
"scoped_app_config_fragments",
2525
"admin_app_config_fragments",

src/ai/backend/manager/api/gql/app_config_fragment/resolver/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
"admin_bulk_purge_app_config_fragments",
1818
"admin_bulk_update_app_config_fragments",
1919
"app_config_fragment",
20-
"my_bulk_create_app_config_fragments",
21-
"my_bulk_update_app_config_fragments",
20+
"bulk_create_my_app_config_fragments",
21+
"bulk_update_my_app_config_fragments",
2222
]

src/ai/backend/manager/api/gql/app_config_fragment/resolver/mutation.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ async def admin_bulk_purge_app_config_fragments(
8989
)
9090
async def my_bulk_create_app_config_fragments(
9191
info: Info[StrawberryGQLContext],
92-
input: MyBulkCreateAppConfigFragmentInputGQL,
93-
) -> MyBulkCreateAppConfigFragmentsPayloadGQL:
92+
input: BulkCreateMyAppConfigFragmentInputGQL,
93+
) -> BulkCreateMyAppConfigFragmentsPayloadGQL:
9494
result = await info.context.adapters.app_config.my_bulk_create(input.to_pydantic())
95-
return MyBulkCreateAppConfigFragmentsPayloadGQL.from_pydantic(result)
95+
return BulkCreateMyAppConfigFragmentsPayloadGQL.from_pydantic(result)
9696

9797

9898
@gql_mutation(
@@ -106,7 +106,7 @@ async def my_bulk_create_app_config_fragments(
106106
)
107107
async def my_bulk_update_app_config_fragments(
108108
info: Info[StrawberryGQLContext],
109-
input: MyBulkUpdateAppConfigFragmentInputGQL,
110-
) -> MyBulkUpdateAppConfigFragmentsPayloadGQL:
109+
input: BulkUpdateMyAppConfigFragmentInputGQL,
110+
) -> BulkUpdateMyAppConfigFragmentsPayloadGQL:
111111
result = await info.context.adapters.app_config.my_bulk_update(input.to_pydantic())
112-
return MyBulkUpdateAppConfigFragmentsPayloadGQL.from_pydantic(result)
112+
return BulkUpdateMyAppConfigFragmentsPayloadGQL.from_pydantic(result)

src/ai/backend/manager/api/gql/app_config_fragment/resolver/query.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""AppConfigFragment GQL query resolvers.
22
3-
Per the scope-bound list is exposed via child fields on
3+
Per BEP-1052 §2 the scope-bound list is exposed via child fields on
44
`DomainV2.appConfigFragments` / `UserV2.appConfigFragments`, not as a
55
root resolver. Only the single-row read and the cross-scope admin
66
search live here. The scope-bound REST endpoint

src/ai/backend/manager/api/gql/schema.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
admin_bulk_purge_app_config_fragments,
2727
admin_bulk_update_app_config_fragments,
2828
app_config_fragment,
29-
my_bulk_create_app_config_fragments,
30-
my_bulk_update_app_config_fragments,
29+
bulk_create_my_app_config_fragments,
30+
bulk_update_my_app_config_fragments,
3131
)
3232
from .app_config_policy import (
3333
admin_bulk_create_app_config_policies,

0 commit comments

Comments
 (0)