From 06a66e8aa0a2a204bb1c1155d67088f30af2d871 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Thu, 16 Nov 2023 14:59:02 +0100 Subject: [PATCH] add user filter startsWith and contains Signed-off-by: Christian Richter --- ...add-user-filter-startswith-and-contains.md | 8 ++ services/graph/pkg/service/v0/users_filter.go | 78 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 changelog/unreleased/add-user-filter-startswith-and-contains.md diff --git a/changelog/unreleased/add-user-filter-startswith-and-contains.md b/changelog/unreleased/add-user-filter-startswith-and-contains.md new file mode 100644 index 000000000..98085d13f --- /dev/null +++ b/changelog/unreleased/add-user-filter-startswith-and-contains.md @@ -0,0 +1,8 @@ +Enhancement: Add user filter startswith and contains + +We add two new filters to the user list endpoint. The `startswith` filter allows to +filter users by the beginning of their name. The `contains` filter allows to +filter users by a substring of their name. + +https://github.com/owncloud/ocis/pull/7739 +https://github.com/owncloud/ocis/issues/5486 diff --git a/services/graph/pkg/service/v0/users_filter.go b/services/graph/pkg/service/v0/users_filter.go index 8448a5605..60e0da923 100644 --- a/services/graph/pkg/service/v0/users_filter.go +++ b/services/graph/pkg/service/v0/users_filter.go @@ -32,11 +32,89 @@ func (g Graph) applyUserFilter(ctx context.Context, req *godata.GoDataRequest, r return g.applyFilterLambda(ctx, req, root.Children) case godata.ExpressionTokenLogical: return g.applyFilterLogical(ctx, req, root) + case godata.ExpressionTokenFunc: + return g.applyFilterFunction(ctx, req, root) } logger.Debug().Str("filter", req.Query.Filter.RawValue).Msg("filter is not supported") return users, unsupportedFilterError() } +func (g Graph) applyFilterFunction(ctx context.Context, req *godata.GoDataRequest, root *godata.ParseNode) (users []*libregraph.User, err error) { + logger := g.logger.SubloggerWithRequestID(ctx) + if root.Token.Type != godata.ExpressionTokenFunc { + return users, invalidFilterError() + } + + switch root.Token.Value { + case "startswith": + // 'startswith' needs 2 operands + if len(root.Children) != 2 { + return users, invalidFilterError() + } + return g.applyFilterFunctionStartsWith(ctx, req, root.Children[0], root.Children[1]), nil + case "contains": + // 'contains' needs 2 operands + if len(root.Children) != 2 { + return users, invalidFilterError() + } + return g.applyFilterFunctionContains(ctx, req, root.Children[0], root.Children[1]), nil + } + logger.Debug().Str("Token", root.Token.Value).Msg("unsupported function filter") + return users, unsupportedFilterError() +} + +func (g Graph) applyFilterFunctionStartsWith(ctx context.Context, req *godata.GoDataRequest, operand1 *godata.ParseNode, operand2 *godata.ParseNode) (users []*libregraph.User) { + logger := g.logger.SubloggerWithRequestID(ctx) + if operand1.Token.Type != godata.ExpressionTokenLiteral { + logger.Debug().Str("Token", operand1.Token.Value).Msg("unsupported function filter") + return users + } + + switch operand1.Token.Value { + case "displayName": + var retUsers []*libregraph.User + filterValue := operand2.Token.Value + logger.Debug().Str("property", operand2.Token.Value).Str("value", filterValue).Msg("Filtering displayName by startsWith") + if users, err := g.identityBackend.GetUsers(ctx, req); err == nil { + for _, user := range users { + if strings.HasPrefix(user.GetDisplayName(), filterValue) { + retUsers = append(retUsers, user) + } + } + } + return retUsers + default: + logger.Warn().Str("Token", operand1.Token.Value).Msg("unsupported function filter") + return nil + } +} + +func (g Graph) applyFilterFunctionContains(ctx context.Context, req *godata.GoDataRequest, operand1 *godata.ParseNode, operand2 *godata.ParseNode) (users []*libregraph.User) { + logger := g.logger.SubloggerWithRequestID(ctx) + if operand1.Token.Type != godata.ExpressionTokenLiteral { + logger.Debug().Str("Token", operand1.Token.Value).Msg("unsupported function filter") + return users + } + + switch operand1.Token.Value { + case "displayName": + var retUsers []*libregraph.User + filterValue := operand2.Token.Value + logger.Debug().Str("property", operand2.Token.Value).Str("value", filterValue).Msg("Filtering displayName by contains") + if users, err := g.identityBackend.GetUsers(ctx, req); err == nil { + for _, user := range users { + if strings.Contains(user.GetDisplayName(), filterValue) { + retUsers = append(retUsers, user) + } + } + } + return retUsers + default: + logger.Warn().Str("Token", operand1.Token.Value).Msg("unsupported function filter") + return nil + } +} + func (g Graph) applyFilterLogical(ctx context.Context, req *godata.GoDataRequest, root *godata.ParseNode) (users []*libregraph.User, err error) { logger := g.logger.SubloggerWithRequestID(ctx) if root.Token.Type != godata.ExpressionTokenLogical {