Skip to content

Commit db84cfe

Browse files
jopemachineclaude
andcommitted
feat(BA-5830): convert AppConfigFragment REST to bulk-only writes (BEP-1052 §3)
Replaces the single-item `create` / `update` / `purge` endpoints with bulk equivalents per BEP-1052 §3 (all writes are bulk; pass a one-element array for a single write). New REST routes on `/v2/app-config-fragments/`: - `POST /bulk-create` — superadmin (admin, any scope) - `POST /bulk-update` — superadmin - `POST /bulk-purge` — superadmin - `POST /my/bulk-create` — auth (USER scope, current_user) - `POST /my/bulk-update` — auth (USER scope, current_user) Removed: `POST /`, `POST /update`, `POST /purge` — replaced by the bulk variants above. Reads are unchanged (`POST /get`, `POST /{scope_type}/{scope_id}/search`, `POST /search`). Handler methods updated to accept the v2 Bulk* DTOs added in BA-5829 (#11285) and return the Bulk*Payload DTOs. `my_bulk_*` resolves the caller via the adapter's `current_user()` call so the REST handler itself stays auth-agnostic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4b52adf commit db84cfe

2 files changed

Lines changed: 62 additions & 30 deletions

File tree

src/ai/backend/manager/api/rest/v2/app_config_fragment/handler.py

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
"""REST v2 handler for the app-config fragment domain (BEP-1052 §2)."""
1+
"""REST v2 handler for the app-config fragment domain (BEP-1052 §4).
2+
3+
Writes are **bulk-only** per BEP §3 — the single-item create / update /
4+
purge endpoints were removed in favour of `/bulk-create`,
5+
`/bulk-update`, `/bulk-purge` (admin) and `/my/bulk-create`,
6+
`/my/bulk-update` (self-service).
7+
"""
28

39
from __future__ import annotations
410

@@ -8,11 +14,13 @@
814

915
from ai.backend.common.api_handlers import APIResponse, BodyParam, PathParam
1016
from ai.backend.common.dto.manager.v2.app_config_fragment.request import (
17+
AdminBulkCreateAppConfigFragmentsInput,
18+
AdminBulkPurgeAppConfigFragmentsInput,
19+
AdminBulkUpdateAppConfigFragmentsInput,
1120
AppConfigFragmentKeyInput,
12-
CreateAppConfigFragmentInput,
13-
PurgeAppConfigFragmentInput,
21+
BulkCreateMyAppConfigFragmentsInput,
22+
BulkUpdateMyAppConfigFragmentsInput,
1423
SearchAppConfigFragmentsInput,
15-
UpdateAppConfigFragmentInput,
1624
)
1725
from ai.backend.common.dto.manager.v2.app_config_fragment.types import AppConfigScopeType
1826
from ai.backend.logging import BraceStyleAdapter
@@ -33,6 +41,8 @@ class V2AppConfigFragmentHandler:
3341
def __init__(self, *, adapter: AppConfigFragmentAdapter) -> None:
3442
self._adapter = adapter
3543

44+
# ── Reads ────────────────────────────────────────────────────
45+
3646
async def get(
3747
self,
3848
body: BodyParam[AppConfigFragmentKeyInput],
@@ -64,28 +74,48 @@ async def admin_search(
6474
result = await self._adapter.admin_search(body.parsed)
6575
return APIResponse.build(status_code=HTTPStatus.OK, response_model=result)
6676

67-
async def admin_create(
77+
# ── Admin bulk writes (BEP-1052 §3) ──────────────────────────
78+
79+
async def admin_bulk_create(
80+
self,
81+
body: BodyParam[AdminBulkCreateAppConfigFragmentsInput],
82+
) -> APIResponse:
83+
"""Strict insert across any scope; per-item transactions (admin only)."""
84+
result = await self._adapter.admin_bulk_create(body.parsed)
85+
return APIResponse.build(status_code=HTTPStatus.OK, response_model=result)
86+
87+
async def admin_bulk_update(
6888
self,
69-
body: BodyParam[CreateAppConfigFragmentInput],
89+
body: BodyParam[AdminBulkUpdateAppConfigFragmentsInput],
7090
) -> APIResponse:
71-
"""Create a new fragment (admin only)."""
72-
result = await self._adapter.create(body.parsed)
73-
return APIResponse.build(status_code=HTTPStatus.CREATED, response_model=result)
91+
"""Wholesale JSON replacement; per-item transactions (admin only)."""
92+
result = await self._adapter.admin_bulk_update(body.parsed)
93+
return APIResponse.build(status_code=HTTPStatus.OK, response_model=result)
94+
95+
async def admin_bulk_purge(
96+
self,
97+
body: BodyParam[AdminBulkPurgeAppConfigFragmentsInput],
98+
) -> APIResponse:
99+
"""Cleanup-only deletion; absent keys are no-oped (admin only)."""
100+
result = await self._adapter.admin_bulk_purge(body.parsed)
101+
return APIResponse.build(status_code=HTTPStatus.OK, response_model=result)
102+
103+
# ── Self-service bulk writes (BEP-1052 §3) ───────────────────
74104

75-
async def admin_update(
105+
async def my_bulk_create(
76106
self,
77-
body: BodyParam[UpdateAppConfigFragmentInput],
107+
body: BodyParam[BulkCreateMyAppConfigFragmentsInput],
78108
) -> APIResponse:
79-
"""Update a fragment's `extra_config` (admin only)."""
80-
result = await self._adapter.update(body.parsed)
109+
"""Self-service bulk create on the caller's `USER` row."""
110+
result = await self._adapter.my_bulk_create(body.parsed)
81111
return APIResponse.build(status_code=HTTPStatus.OK, response_model=result)
82112

83-
async def admin_purge(
113+
async def my_bulk_update(
84114
self,
85-
body: BodyParam[PurgeAppConfigFragmentInput],
115+
body: BodyParam[BulkUpdateMyAppConfigFragmentsInput],
86116
) -> APIResponse:
87-
"""Hard-delete a fragment (admin only)."""
88-
result = await self._adapter.purge(body.parsed)
117+
"""Self-service bulk update on the caller's `USER` row."""
118+
result = await self._adapter.my_bulk_update(body.parsed)
89119
return APIResponse.build(status_code=HTTPStatus.OK, response_model=result)
90120

91121

src/ai/backend/manager/api/rest/v2/app_config_fragment/registry.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Route registration for v2 app-config fragment endpoints."""
1+
"""Route registration for v2 app-config fragment endpoints (BEP-1052 §4)."""
22

33
from __future__ import annotations
44

@@ -19,17 +19,16 @@ def register_v2_app_config_fragment_routes(
1919
) -> RouteRegistry:
2020
"""Register all v2 app-config fragment routes (BEP-1052 §4).
2121
22-
- `POST /v2/app-config-fragments/get` reads a single row via body
23-
(the natural key has three fields, so a body DTO is cleaner than
24-
a long path).
25-
- Scoped search mounts at
26-
`/v2/app-config-fragments/{scope_type}/{scope_id}/search` per the
27-
project's scoped-search URL convention.
28-
- Admin cross-scope search, create / update / purge are admin-only.
22+
- `POST /get` reads a single row via body (three-field natural key).
23+
- Scoped search mounts at `/{scope_type}/{scope_id}/search`.
24+
- Admin cross-scope search + bulk writes are admin-only.
25+
- `/my/bulk-create` and `/my/bulk-update` are self-service writes
26+
on the caller's `USER` row (no `/my/bulk-purge` — admin-only
27+
cleanup per BEP-1052 §3).
2928
"""
3029
reg = RouteRegistry.create("app-config-fragments", route_deps.cors_options)
3130

32-
# Read
31+
# Reads
3332
reg.add("POST", "/get", handler.get, middlewares=[auth_required])
3433
reg.add(
3534
"POST",
@@ -38,9 +37,12 @@ def register_v2_app_config_fragment_routes(
3837
middlewares=[auth_required],
3938
)
4039
reg.add("POST", "/search", handler.admin_search, middlewares=[superadmin_required])
41-
# Write
42-
reg.add("POST", "", handler.admin_create, middlewares=[superadmin_required])
43-
reg.add("POST", "/update", handler.admin_update, middlewares=[superadmin_required])
44-
reg.add("POST", "/purge", handler.admin_purge, middlewares=[superadmin_required])
40+
# Admin bulk writes (BEP-1052 §3 — bulk-only)
41+
reg.add("POST", "/bulk-create", handler.admin_bulk_create, middlewares=[superadmin_required])
42+
reg.add("POST", "/bulk-update", handler.admin_bulk_update, middlewares=[superadmin_required])
43+
reg.add("POST", "/bulk-purge", handler.admin_bulk_purge, middlewares=[superadmin_required])
44+
# Self-service bulk writes
45+
reg.add("POST", "/my/bulk-create", handler.my_bulk_create, middlewares=[auth_required])
46+
reg.add("POST", "/my/bulk-update", handler.my_bulk_update, middlewares=[auth_required])
4547

4648
return reg

0 commit comments

Comments
 (0)