Skip to content

Commit b74b095

Browse files
jopemachineclaude
andcommitted
fix(BA-5815): use AppConfigPolicyConnection + add updated_at order field
- `AppConfigPolicyGQL` is now a Strawberry Relay Node (`PydanticNodeMixin[AppConfigPolicyNode]`) so it can flow through `Edge[T]` / `Connection[T]`. - Add `AppConfigPolicyEdgeGQL` and `AppConfigPolicyConnectionGQL` and return the connection from `appConfigPolicies` (consistent with `auditLogsV2` and other paginated queries). - Add `UPDATED_AT` to `AppConfigPolicyOrderFieldGQL`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b596ba3 commit b74b095

4 files changed

Lines changed: 55 additions & 11 deletions

File tree

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22

33
from __future__ import annotations
44

5+
import strawberry
56
from strawberry import Info
67

78
from ai.backend.common.dto.manager.v2.app_config_policy.request import (
89
SearchAppConfigPoliciesInput,
910
)
1011
from ai.backend.common.meta.meta import NEXT_RELEASE_VERSION
1112
from ai.backend.manager.api.gql.app_config_policy.types import (
13+
AppConfigPolicyConnectionGQL,
14+
AppConfigPolicyEdgeGQL,
1215
AppConfigPolicyFilterGQL,
1316
AppConfigPolicyGQL,
1417
AppConfigPolicyOrderByGQL,
1518
)
19+
from ai.backend.manager.api.gql.base import encode_cursor
1620
from ai.backend.manager.api.gql.decorators import (
1721
BackendAIGQLMeta,
1822
gql_root_field,
@@ -57,7 +61,7 @@ async def app_config_policies(
5761
before: str | None = None,
5862
limit: int | None = None,
5963
offset: int | None = None,
60-
) -> list[AppConfigPolicyGQL]:
64+
) -> AppConfigPolicyConnectionGQL:
6165
pydantic_filter = filter.to_pydantic() if filter else None
6266
pydantic_order = [o.to_pydantic() for o in order_by] if order_by else None
6367

@@ -73,4 +77,17 @@ async def app_config_policies(
7377
offset=offset,
7478
)
7579
)
76-
return [AppConfigPolicyGQL.from_pydantic(node) for node in payload.items]
80+
nodes = [AppConfigPolicyGQL.from_pydantic(node) for node in payload.items]
81+
edges = [
82+
AppConfigPolicyEdgeGQL(node=node, cursor=encode_cursor(str(node.id))) for node in nodes
83+
]
84+
return AppConfigPolicyConnectionGQL(
85+
edges=edges,
86+
page_info=strawberry.relay.PageInfo(
87+
has_next_page=payload.has_next_page,
88+
has_previous_page=payload.has_previous_page,
89+
start_cursor=edges[0].cursor if edges else None,
90+
end_cursor=edges[-1].cursor if edges else None,
91+
),
92+
count=payload.total_count,
93+
)

src/ai/backend/manager/api/gql/app_config_policy/types/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
AppConfigPolicyOrderByGQL,
1616
AppConfigPolicyOrderFieldGQL,
1717
)
18-
from .node import AppConfigPolicyGQL
18+
from .node import (
19+
AppConfigPolicyConnectionGQL,
20+
AppConfigPolicyEdgeGQL,
21+
AppConfigPolicyGQL,
22+
)
1923

2024
__all__ = [
2125
"AdminAppConfigPolicyItemInputGQL",
@@ -26,6 +30,8 @@
2630
"AdminBulkUpdateAppConfigPoliciesPayloadGQL",
2731
"AdminBulkUpdateAppConfigPolicyInputGQL",
2832
"AppConfigPolicyBulkErrorGQL",
33+
"AppConfigPolicyConnectionGQL",
34+
"AppConfigPolicyEdgeGQL",
2935
"AppConfigPolicyFilterGQL",
3036
"AppConfigPolicyGQL",
3137
"AppConfigPolicyOrderByGQL",

src/ai/backend/manager/api/gql/app_config_policy/types/filters.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class AppConfigPolicyFilterGQL(PydanticInputMixin[AppConfigPolicyFilterDTO]):
4545
class AppConfigPolicyOrderFieldGQL(StrEnum):
4646
CONFIG_NAME = "config_name"
4747
CREATED_AT = "created_at"
48+
UPDATED_AT = "updated_at"
4849

4950

5051
@gql_pydantic_input(
Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,53 @@
1-
"""AppConfigPolicy GQL output type."""
1+
"""AppConfigPolicy GraphQL Node, Edge, and Connection types."""
22

33
from __future__ import annotations
44

55
from datetime import datetime
6-
from uuid import UUID
6+
from typing import Any
7+
8+
from strawberry.relay import Connection, Edge, NodeID
79

810
from ai.backend.common.dto.manager.v2.app_config_policy.response import AppConfigPolicyNode
911
from ai.backend.common.meta.meta import NEXT_RELEASE_VERSION
1012
from ai.backend.manager.api.gql.decorators import (
1113
BackendAIGQLMeta,
14+
gql_connection_type,
1215
gql_field,
13-
gql_pydantic_type,
16+
gql_node_type,
1417
)
15-
from ai.backend.manager.api.gql.pydantic_compat import PydanticOutputMixin
18+
from ai.backend.manager.api.gql.pydantic_compat import PydanticNodeMixin
1619

1720

18-
@gql_pydantic_type(
21+
@gql_node_type(
1922
BackendAIGQLMeta(
2023
added_version=NEXT_RELEASE_VERSION,
2124
description="Scoped app-config policy (BEP-1052 §1).",
2225
),
23-
model=AppConfigPolicyNode,
2426
name="AppConfigPolicy",
2527
)
26-
class AppConfigPolicyGQL(PydanticOutputMixin[AppConfigPolicyNode]):
27-
id: UUID = gql_field(description="Policy row UUID.")
28+
class AppConfigPolicyGQL(PydanticNodeMixin[AppConfigPolicyNode]):
29+
id: NodeID[str] = gql_field(description="Policy row UUID.")
2830
config_name: str = gql_field(description="Unique, immutable policy name.")
2931
scope_sources: list[str] = gql_field(
3032
description="Ordered scope chain (low → high merge priority).",
3133
)
3234
created_at: datetime = gql_field(description="Creation timestamp.")
3335
updated_at: datetime | None = gql_field(description="Last update timestamp.")
36+
37+
38+
AppConfigPolicyEdgeGQL = Edge[AppConfigPolicyGQL]
39+
40+
41+
@gql_connection_type(
42+
BackendAIGQLMeta(
43+
added_version=NEXT_RELEASE_VERSION,
44+
description="Connection type for paginated app-config policy results.",
45+
),
46+
name="AppConfigPolicyConnection",
47+
)
48+
class AppConfigPolicyConnectionGQL(Connection[AppConfigPolicyGQL]):
49+
count: int = gql_field(description="Total number of policies matching the query.")
50+
51+
def __init__(self, *args: Any, count: int, **kwargs: Any) -> None:
52+
super().__init__(*args, **kwargs)
53+
self.count = count

0 commit comments

Comments
 (0)