Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(BA-596): Add project_node field to GQL UserNode type #3529

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/3529.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `project_node` field of type GroupConnection to GQL `UserNode` type
3 changes: 3 additions & 0 deletions docs/manager/graphql-reference/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -732,6 +732,9 @@
totp_activated: Boolean
totp_activated_at: DateTime
sudo_session_enabled: Boolean

"""Added in 25.2.0."""
project_nodes(filter: String, order: String, offset: Int, before: String, after: String, first: Int, last: Int): GroupConnection

Check notice on line 737 in docs/manager/graphql-reference/schema.graphql

View workflow job for this annotation

GitHub Actions / GraphQL Inspector

Field 'project_nodes' was added to object type 'UserNode'

Field 'project_nodes' was added to object type 'UserNode'
}

"""Added in 24.03.0"""
Expand Down
2 changes: 1 addition & 1 deletion docs/manager/rest-reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"info": {
"title": "Backend.AI Manager API",
"description": "Backend.AI Manager REST API specification",
"version": "24.12.1",
"version": "25.1.1",
"contact": {
"name": "Lablup Inc.",
"url": "https://docs.backend.ai",
Expand Down
48 changes: 24 additions & 24 deletions src/ai/backend/manager/models/gql_models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,6 @@
from ..gql import GraphQueryContext
from ..scaling_group import ScalingGroup

_queryfilter_fieldspec: Mapping[str, FieldSpecItem] = {
"id": ("id", None),
"row_id": ("id", None),
"name": ("name", None),
"is_active": ("is_active", None),
"created_at": ("created_at", dtparse),
"modified_at": ("modified_at", dtparse),
"domain_name": ("domain_name", None),
"resource_policy": ("resource_policy", None),
}

_queryorder_colmap: Mapping[str, OrderSpecItem] = {
"id": ("id", None),
"row_id": ("id", None),
"name": ("name", None),
"is_active": ("is_active", None),
"created_at": ("created_at", None),
"modified_at": ("modified_at", None),
"domain_name": ("domain_name", None),
"resource_policy": ("resource_policy", None),
}


class GroupInput(graphene.InputObjectType):
type = graphene.String(
Expand Down Expand Up @@ -116,6 +94,28 @@ class Meta:
UserConnection,
)

queryfilter_fieldspec: Mapping[str, FieldSpecItem] = {
"id": ("id", None),
"row_id": ("id", None),
"name": ("name", None),
"is_active": ("is_active", None),
"created_at": ("created_at", dtparse),
"modified_at": ("modified_at", dtparse),
"domain_name": ("domain_name", None),
"resource_policy": ("resource_policy", None),
}

queryorder_colmap: Mapping[str, OrderSpecItem] = {
"id": ("id", None),
"row_id": ("id", None),
"name": ("name", None),
"is_active": ("is_active", None),
"created_at": ("created_at", None),
"modified_at": ("modified_at", None),
"domain_name": ("domain_name", None),
"resource_policy": ("resource_policy", None),
}

@classmethod
def from_row(
cls,
Expand Down Expand Up @@ -227,12 +227,12 @@ async def get_connection(
) -> ConnectionResolverResult[Self]:
graph_ctx: GraphQueryContext = info.context
_filter_arg = (
FilterExprArg(filter_expr, QueryFilterParser(_queryfilter_fieldspec))
FilterExprArg(filter_expr, QueryFilterParser(cls.queryfilter_fieldspec))
if filter_expr is not None
else None
)
_order_expr = (
OrderExprArg(order_expr, QueryOrderParser(_queryorder_colmap))
OrderExprArg(order_expr, QueryOrderParser(cls.queryorder_colmap))
if order_expr is not None
else None
)
Expand Down
64 changes: 64 additions & 0 deletions src/ai/backend/manager/models/gql_models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from typing import (
TYPE_CHECKING,
Mapping,
Optional,
Self,
cast,
)

import graphene
Expand All @@ -14,6 +16,7 @@
from ..base import (
FilterExprArg,
OrderExprArg,
PaginatedConnectionField,
generate_sql_info_for_gql_connection,
)
from ..gql_relay import AsyncNode, Connection, ConnectionResolverResult
Expand All @@ -23,6 +26,7 @@

if TYPE_CHECKING:
from ..gql import GraphQueryContext
from .group import GroupNode


class UserNode(graphene.ObjectType):
Expand Down Expand Up @@ -53,6 +57,11 @@ class Meta:
totp_activated_at = GQLDateTime()
sudo_session_enabled = graphene.Boolean()

project_nodes = PaginatedConnectionField(
"ai.backend.manager.models.gql_models.group.GroupConnection",
description="Added in 25.2.0.",
)

@classmethod
def from_row(cls, ctx: GraphQueryContext, row: UserRow) -> Self:
return cls(
Expand Down Expand Up @@ -176,6 +185,61 @@ async def get_connection(
total_cnt = await db_session.scalar(cnt_query)
return ConnectionResolverResult(result, cursor, pagination_order, page_size, total_cnt)

async def resolve_project_nodes(
self,
info: graphene.ResolveInfo,
filter: Optional[str] = None,
order: Optional[str] = None,
offset: Optional[int] = None,
after: Optional[str] = None,
first: Optional[int] = None,
before: Optional[str] = None,
last: Optional[int] = None,
) -> ConnectionResolverResult[GroupNode]:
from ..group import AssocGroupUserRow, GroupRow
from .group import GroupNode

graph_ctx: GraphQueryContext = info.context
_filter_arg = (
FilterExprArg(filter, QueryFilterParser(GroupNode.queryfilter_fieldspec))
if filter is not None
else None
)
_order_expr = (
OrderExprArg(order, QueryOrderParser(GroupNode.queryorder_colmap))
if order is not None
else None
)
(
query,
cnt_query,
_,
cursor,
pagination_order,
page_size,
) = generate_sql_info_for_gql_connection(
info,
GroupRow,
GroupRow.id,
_filter_arg,
_order_expr,
offset,
after=after,
first=first,
before=before,
last=last,
)
j = sa.join(GroupRow, AssocGroupUserRow)
prj_query = query.select_from(j).where(AssocGroupUserRow.user_id == self.id)
cnt_query = cnt_query.select_from(j).where(AssocGroupUserRow.user_id == self.id)
result: list[GroupNode] = []
async with graph_ctx.db.begin_readonly_session() as db_session:
total_cnt = await db_session.scalar(cnt_query)
async for row in await db_session.stream_scalars(prj_query):
prj_row = cast(GroupRow, row)
result.append(GroupNode.from_row(graph_ctx, prj_row))
return ConnectionResolverResult(result, cursor, pagination_order, page_size, total_cnt)


class UserConnection(Connection):
class Meta:
Expand Down
Loading