Add search and status/role/type filters to People

Makes it practical to find people in larger orgs across GraphQL, MCP, CLI, and n8n, with page size raised to 100.

Signed-off-by: Sacha Al Himdani <sacha@probo.com>
This commit is contained in:
Sacha Al Himdani
2026-07-27 14:46:15 +02:00
parent edb67743ae
commit d61ec8dd65
19 changed files with 624 additions and 123 deletions

View File

@@ -12,6 +12,7 @@ type Organization implements Node {
last: Int
before: CursorKey
orderBy: ProfileOrder
filter: ProfileFilter
): ProfileConnection @goField(forceResolver: true)
samlConfigurations(

View File

@@ -71,6 +71,9 @@ enum ProfileOrderField
input ProfileFilter {
contractEnded: Boolean
state: ProfileState
query: String
role: MembershipRole
kind: String
}
input ProfileOrder

View File

@@ -31,9 +31,22 @@ func (r *identityResolver) Profiles(ctx context.Context, obj *types.Identity, fi
filters := coredata.NewMembershipProfileFilter(nil).WithMembership()
if filter != nil {
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
if filter.State != nil {
filters.WithState(*filter.State)
}
if filter.Query != nil {
filters.WithQuery(filter.Query)
}
if filter.Role != nil {
filters.WithRole(*filter.Role)
}
if filter.Kind != nil {
filters.WithKind(filter.Kind)
}
}
if gqlutils.OnlyTotalCountSelected(ctx) {

View File

@@ -179,18 +179,37 @@ func (r *organizationResolver) HorizontalLogo(ctx context.Context, obj *types.Or
}
// Profiles is the resolver for the profiles field.
func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProfileOrderBy) (*types.ProfileConnection, error) {
func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ProfileOrderBy, filter *types.ProfileFilter) (*types.ProfileConnection, error) {
if _, err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileList); err != nil {
return nil, err
}
filter := coredata.NewMembershipProfileFilter(nil).WithMembership()
filters := coredata.NewMembershipProfileFilter(nil).WithMembership()
if filter != nil {
filters = coredata.NewMembershipProfileFilter(filter.ContractEnded).WithMembership()
if filter.State != nil {
filters.WithState(*filter.State)
}
if filter.Query != nil {
filters.WithQuery(filter.Query)
}
if filter.Role != nil {
filters.WithRole(*filter.Role)
}
if filter.Kind != nil {
filters.WithKind(filter.Kind)
}
}
if gqlutils.OnlyTotalCountSelected(ctx) {
return &types.ProfileConnection{
Resolver: r,
ParentID: obj.ID,
Filters: filter,
Filters: filters,
}, nil
}
@@ -207,13 +226,13 @@ func (r *organizationResolver) Profiles(ctx context.Context, obj *types.Organiza
cursor := cursor.NewCursor(first, after, last, before, pageOrderBy)
page, err := r.iam.OrganizationService.ListProfiles(ctx, obj.ID, cursor, filter)
page, err := r.iam.OrganizationService.ListProfiles(ctx, obj.ID, cursor, filters)
if err != nil {
r.logger.ErrorCtx(ctx, "cannot list profiles", log.Error(err))
return nil, gqlutils.Internal(ctx)
}
return types.NewProfileConnection(page, r, obj.ID, filter), nil
return types.NewProfileConnection(page, r, obj.ID, filters), nil
}
// SamlConfigurations is the resolver for the samlConfigurations field.