feat(BA-4363): Implement RBAC GQL fetchers, resolvers, and type field resolvers#8900
feat(BA-4363): Implement RBAC GQL fetchers, resolvers, and type field resolvers#8900
Conversation
35faf94 to
4e611b4
Compare
There was a problem hiding this comment.
Pull request overview
This PR implements 27 NotImplementedError stubs across RBAC GraphQL fetchers, resolvers, and type field resolvers introduced in PR #8746, completing the GraphQL API for RBAC management.
Changes:
- Implemented type field resolvers (resolve_nodes, role(), user(), scope()) using data loaders for efficient batch loading of related entities
- Implemented query resolvers that delegate to fetchers (admin_role, admin_roles, admin_role_assignments, admin_permissions, scope_types, entity_types, admin_entities)
- Implemented mutation resolvers that call permission controller processors directly (create, update, delete, purge, assign, revoke roles; create, delete permissions)
- Implemented fetchers that build Relay-style paginated connections using permission controller processors
- Updated GraphQL schema to mark admin_entities as nullable for unsupported entity types
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/ai/backend/manager/api/gql/rbac/types/role.py | Implements resolve_nodes for RoleGQL and RoleAssignmentGQL using data loaders; adds role() and user() field resolvers |
| src/ai/backend/manager/api/gql/rbac/types/permission.py | Implements resolve_nodes for PermissionGQL, role() field resolver, and comprehensive scope() resolver handling 7 scope types with data loaders |
| src/ai/backend/manager/api/gql/rbac/resolver/role.py | Implements 3 query resolvers delegating to fetchers and 6 mutation resolvers calling permission controller processors |
| src/ai/backend/manager/api/gql/rbac/resolver/permission.py | Implements 3 query resolvers (admin_permissions, scope_types, entity_types) and 2 mutation resolvers (create, delete) |
| src/ai/backend/manager/api/gql/rbac/resolver/entity.py | Implements admin_entities resolver delegating to entity fetcher, returns None for unsupported types |
| src/ai/backend/manager/api/gql/rbac/fetcher/role.py | Implements fetch_role, fetch_roles, and fetch_role_assignments with Relay-style pagination via permission controller processors |
| src/ai/backend/manager/api/gql/rbac/fetcher/permission.py | Implements fetch_permissions with Relay-style pagination using permission controller search action |
| src/ai/backend/manager/api/gql/rbac/fetcher/entity.py | Implements fetch_entities supporting USER, PROJECT, DOMAIN, ROLE, IMAGE, ARTIFACT, ARTIFACT_REVISION, DEPLOYMENT types; returns None for unsupported types |
| docs/manager/graphql-reference/v2-schema.graphql | Updates admin_entities return type from EntityConnection! to EntityConnection (nullable) |
| docs/manager/graphql-reference/supergraph.graphql | Updates admin_entities return type from EntityConnection! to EntityConnection (nullable) |
| changes/8900.feature.md | Adds changelog entry for RBAC GQL implementation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @strawberry.field(description="Added in 26.3.0. List available scope types.") # type: ignore[misc] | ||
| async def scope_types( | ||
| info: Info[StrawberryGQLContext], | ||
| ) -> list[EntityTypeGQL]: | ||
| raise NotImplementedError | ||
| action_result = ( | ||
| await info.context.processors.permission_controller.get_scope_types.wait_for_complete( | ||
| GetScopeTypesAction() | ||
| ) | ||
| ) | ||
| return [EntityTypeGQL.from_scope_type(st) for st in action_result.scope_types] | ||
|
|
||
|
|
||
| @strawberry.field(description="Added in 26.3.0. List available entity types.") # type: ignore[misc] | ||
| async def entity_types( | ||
| info: Info[StrawberryGQLContext], | ||
| ) -> list[EntityTypeGQL]: | ||
| raise NotImplementedError | ||
| action_result = ( | ||
| await info.context.processors.permission_controller.get_entity_types.wait_for_complete( | ||
| GetEntityTypesAction() | ||
| ) | ||
| ) | ||
| return [EntityTypeGQL.from_internal(et) for et in action_result.entity_types] | ||
|
|
There was a problem hiding this comment.
If we provide this as an enum, wouldn't we be able to identify the types even without it? What do you think?
There was a problem hiding this comment.
I removed the query because it is not needed as you said
| case ScopeType.ARTIFACT: | ||
| return None | ||
| case ScopeType.ARTIFACT_REGISTRY: | ||
| return None | ||
| case ScopeType.IMAGE: | ||
| return None | ||
| case ScopeType.CONTAINER_REGISTRY: | ||
| return None | ||
| case ScopeType.STORAGE_HOST: | ||
| return None | ||
| case ScopeType.SESSION: | ||
| return None | ||
| case ScopeType.VFOLDER: | ||
| return None | ||
|
|
There was a problem hiding this comment.
Why is this section empty?
There was a problem hiding this comment.
because the types do not have any dataloader function
… resolvers Implement all 27 stub functions across fetchers (5), resolvers (15), and type field resolvers (7) that were added as NotImplementedError stubs in PR #8746. Fetchers call permission_controller processors and build Relay-style connections with cursor-based pagination. Resolvers delegate to fetchers for queries and call processors directly for mutations. Type field resolvers use data loaders for efficient batch loading of related entities (roles, users, permissions, assignments). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve scope entities via data loaders based on scope_type, supporting USER, PROJECT, DOMAIN, ROLE, RESOURCE_GROUP, DEPLOYMENT, and ARTIFACT_REVISION. Exhaustively match all ScopeType variants to enable mypy detection of unhandled cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…c fetchers Dispatch fetch_entities() to dedicated fetchers (fetch_admin_users, fetch_admin_projects, etc.) instead of going through the entity_scope table via search_entities action. Remove _load_entity_node() which is no longer needed. Exhaustively match all EntityTypeGQL members. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: octodog <mu001@lablup.com>
…e repetition Replace 8 repeated _convert(...) call sites with a single _convert_connection(conn) helper that accepts any connection object matching the _PaginatedConnection protocol. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…geLike Protocol Replace Any with a structural _EdgeLike Protocol for the edges property, ensuring proper type checking of node and cursor accesses in _convert_connection(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dde07f1 to
9c69787
Compare
Co-authored-by: octodog <mu001@lablup.com>
These are unnecessary since EntityTypeGQL is already a strawberry enum that clients can introspect directly. REST API endpoints are retained. Also add TODO comment for unimplemented scope data loaders. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: octodog <mu001@lablup.com>
Revert the EntityNode→EntityRefGQL change from the earlier refactoring. EntityRefGQL is the correct node type for EntityEdge, representing an entity reference from the association_scopes_entities table. Also restores EntityFilter, EntityOrderBy, and the stub-based fetcher/resolver signatures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: octodog <mu001@lablup.com>
resolves #8750 (BA-4363)
Summary
📚 Documentation preview 📚: https://sorna--8900.org.readthedocs.build/en/8900/
📚 Documentation preview 📚: https://sorna-ko--8900.org.readthedocs.build/ko/8900/