mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:37:32 +00:00
Add convert method for ListUsersByMetadata request
This commit is contained in:
115
internal/api/grpc/user/v2beta/convert/metadata.go
Normal file
115
internal/api/grpc/user/v2beta/convert/metadata.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
metadata "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func ListUsersByMetadataRequestToModel(req *user.ListUsersByMetadataRequest, sysDefaults systemdefaults.SystemDefaults) (*query.UserSearchQueries, error) {
|
||||
offset, limit, asc, err := filter.PaginationPbToQuery(sysDefaults, req.GetPagination())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queries, err := usersByMetadataQueries(req.GetFilters(), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &query.UserSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: usersByMetadataSorting(req.GetSortingColumn()),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func usersByMetadataSorting(sortingColumn user.UsersByMetadataSorting) query.Column {
|
||||
switch sortingColumn {
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_DISPLAY_NAME:
|
||||
return query.HumanDisplayNameCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_EMAIL:
|
||||
return query.HumanEmailCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_FIRST_NAME:
|
||||
return query.HumanFirstNameCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_LAST_NAME:
|
||||
return query.HumanLastNameCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_METADATA_KEY:
|
||||
return query.UserMetadataKeyCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_METADATA_VALUE:
|
||||
return query.UserMetadataValueCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_NICK_NAME:
|
||||
return query.HumanNickNameCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_STATE:
|
||||
return query.UserStateCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_TYPE:
|
||||
return query.UserTypeCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_USER_NAME:
|
||||
return query.UserUsernameCol
|
||||
case user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_USER_ID, user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_UNSPECIFIED:
|
||||
fallthrough
|
||||
default:
|
||||
return query.UserIDCol
|
||||
}
|
||||
}
|
||||
|
||||
func usersByMetadataQueries(queries []*metadata.UserByMetadataSearchFilter, nesting uint) ([]query.SearchQuery, error) {
|
||||
toReturn := make([]query.SearchQuery, len(queries))
|
||||
|
||||
for i, query := range queries {
|
||||
res, err := userByMetadataQuery(query, nesting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
toReturn[i] = res
|
||||
}
|
||||
|
||||
return toReturn, nil
|
||||
}
|
||||
|
||||
func userByMetadataQuery(q *metadata.UserByMetadataSearchFilter, nesting uint) (query.SearchQuery, error) {
|
||||
if nesting > 20 {
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "CONV-Jhaltm", "Errors.Query.TooManyNestingLevels")
|
||||
}
|
||||
|
||||
switch t := q.GetFilter().(type) {
|
||||
|
||||
case *metadata.UserByMetadataSearchFilter_KeyFilter:
|
||||
return query.NewUserMetadataKeySearchQuery(t.KeyFilter.GetKey(), filter.TextMethodPbToQuery(t.KeyFilter.GetMethod()))
|
||||
|
||||
case *metadata.UserByMetadataSearchFilter_ValueFilter:
|
||||
return query.NewUserMetadataValueSearchQuery(t.ValueFilter.GetValue(), filter.ByteMethodPbToQuery(t.ValueFilter.GetMethod()))
|
||||
|
||||
case *metadata.UserByMetadataSearchFilter_AndFilter:
|
||||
mappedQueries, err := usersByMetadataQueries(t.AndFilter.GetQueries(), nesting+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserMetadataAndSearchQuery(mappedQueries)
|
||||
|
||||
case *metadata.UserByMetadataSearchFilter_OrFilter:
|
||||
mappedQueries, err := usersByMetadataQueries(t.OrFilter.GetQueries(), nesting+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserMetadataOrSearchQuery(mappedQueries)
|
||||
|
||||
case *metadata.UserByMetadataSearchFilter_NotFilter:
|
||||
mappedQuery, err := userByMetadataQuery(t.NotFilter.GetQuery(), nesting+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserMetadataNotSearchQuery(mappedQuery)
|
||||
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "CONV-GG1Jnh", "List.Query.Invalid")
|
||||
}
|
||||
}
|
273
internal/api/grpc/user/v2beta/convert/metadata_test.go
Normal file
273
internal/api/grpc/user/v2beta/convert/metadata_test.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
filter "github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
||||
metadata "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
|
||||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2beta"
|
||||
)
|
||||
|
||||
func Test_usersByMetadataSorting(t *testing.T) {
|
||||
t.Parallel()
|
||||
tt := []struct {
|
||||
name string
|
||||
input user.UsersByMetadataSorting
|
||||
expected query.Column
|
||||
}{
|
||||
{"DisplayName", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_DISPLAY_NAME, query.HumanDisplayNameCol},
|
||||
{"Email", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_EMAIL, query.HumanEmailCol},
|
||||
{"FirstName", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_FIRST_NAME, query.HumanFirstNameCol},
|
||||
{"LastName", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_LAST_NAME, query.HumanLastNameCol},
|
||||
{"MetadataKey", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_METADATA_KEY, query.UserMetadataKeyCol},
|
||||
{"MetadataValue", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_METADATA_VALUE, query.UserMetadataValueCol},
|
||||
{"NickName", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_NICK_NAME, query.HumanNickNameCol},
|
||||
{"State", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_STATE, query.UserStateCol},
|
||||
{"Type", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_TYPE, query.UserTypeCol},
|
||||
{"UserName", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_USER_NAME, query.UserUsernameCol},
|
||||
{"UserID", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_USER_ID, query.UserIDCol},
|
||||
{"Unspecified", user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_UNSPECIFIED, query.UserIDCol},
|
||||
{"Default", user.UsersByMetadataSorting(999), query.UserIDCol},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
got := usersByMetadataSorting(tc.input)
|
||||
assert.Equal(t, tc.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_userByMetadataQuery(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tt := []struct {
|
||||
name string
|
||||
input *metadata.UserByMetadataSearchFilter
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "KeyFilter",
|
||||
input: &metadata.UserByMetadataSearchFilter{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_KeyFilter{
|
||||
KeyFilter: &metadata.MetadataKeyFilter{
|
||||
Key: "foo",
|
||||
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "ValueFilter",
|
||||
input: &metadata.UserByMetadataSearchFilter{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_ValueFilter{
|
||||
ValueFilter: &metadata.MetadataValueFilter{
|
||||
Value: []byte("bar"),
|
||||
Method: filter.ByteFilterMethod_BYTE_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "AndFilter",
|
||||
input: &metadata.UserByMetadataSearchFilter{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_AndFilter{
|
||||
AndFilter: &metadata.MetadataAndFilter{
|
||||
Queries: []*metadata.UserByMetadataSearchFilter{
|
||||
{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_KeyFilter{
|
||||
KeyFilter: &metadata.MetadataKeyFilter{
|
||||
Key: "foo",
|
||||
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "OrFilter",
|
||||
input: &metadata.UserByMetadataSearchFilter{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_OrFilter{
|
||||
OrFilter: &metadata.MetadataOrFilter{
|
||||
Queries: []*metadata.UserByMetadataSearchFilter{
|
||||
{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_ValueFilter{
|
||||
ValueFilter: &metadata.MetadataValueFilter{
|
||||
Value: []byte("baz"),
|
||||
Method: filter.ByteFilterMethod_BYTE_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "NotFilter",
|
||||
input: &metadata.UserByMetadataSearchFilter{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_NotFilter{
|
||||
NotFilter: &metadata.MetadataNotFilter{
|
||||
Query: &metadata.UserByMetadataSearchFilter{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_KeyFilter{
|
||||
KeyFilter: &metadata.MetadataKeyFilter{
|
||||
Key: "not",
|
||||
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "TooManyNestingLevels",
|
||||
input: &metadata.UserByMetadataSearchFilter{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_AndFilter{
|
||||
AndFilter: &metadata.MetadataAndFilter{
|
||||
Queries: []*metadata.UserByMetadataSearchFilter{},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "InvalidFilter",
|
||||
input: &metadata.UserByMetadataSearchFilter{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
nesting := uint(0)
|
||||
if tc.name == "TooManyNestingLevels" {
|
||||
nesting = 21
|
||||
}
|
||||
got, err := userByMetadataQuery(tc.input, nesting)
|
||||
if tc.wantErr {
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, got)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_usersByMetadataQueries(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
queries, err := usersByMetadataQueries([]*metadata.UserByMetadataSearchFilter{}, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, queries, 0)
|
||||
})
|
||||
|
||||
t.Run("single valid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
queries, err := usersByMetadataQueries([]*metadata.UserByMetadataSearchFilter{
|
||||
{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_KeyFilter{
|
||||
KeyFilter: &metadata.MetadataKeyFilter{
|
||||
Key: "foo",
|
||||
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, 0)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, queries, 1)
|
||||
})
|
||||
|
||||
t.Run("invalid filter", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
queries, err := usersByMetadataQueries([]*metadata.UserByMetadataSearchFilter{
|
||||
{},
|
||||
}, 0)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, queries)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListUsersByMetadataRequestToModel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sysDefaults := systemdefaults.SystemDefaults{
|
||||
MaxQueryLimit: 100,
|
||||
}
|
||||
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
req := &user.ListUsersByMetadataRequest{
|
||||
Pagination: &filter.PaginationRequest{
|
||||
Limit: 10,
|
||||
Offset: 5,
|
||||
Asc: true,
|
||||
},
|
||||
SortingColumn: user.UsersByMetadataSorting_USERS_BY_METADATA_SORT_BY_EMAIL,
|
||||
Filters: []*metadata.UserByMetadataSearchFilter{
|
||||
{
|
||||
Filter: &metadata.UserByMetadataSearchFilter_KeyFilter{
|
||||
KeyFilter: &metadata.MetadataKeyFilter{
|
||||
Key: "foo",
|
||||
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
model, err := ListUsersByMetadataRequestToModel(req, sysDefaults)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, model)
|
||||
assert.EqualValues(t, 5, model.Offset)
|
||||
assert.EqualValues(t, 10, model.Limit)
|
||||
assert.True(t, model.Asc)
|
||||
assert.Equal(t, query.HumanEmailCol, model.SortingColumn)
|
||||
assert.Len(t, model.Queries, 1)
|
||||
})
|
||||
|
||||
t.Run("invalid pagination", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
req := &user.ListUsersByMetadataRequest{
|
||||
Pagination: &filter.PaginationRequest{
|
||||
|
||||
Limit: 200,
|
||||
},
|
||||
Filters: []*metadata.UserByMetadataSearchFilter{},
|
||||
}
|
||||
model, err := ListUsersByMetadataRequestToModel(req, sysDefaults)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, model)
|
||||
})
|
||||
|
||||
t.Run("invalid filter", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
req := &user.ListUsersByMetadataRequest{
|
||||
Pagination: &filter.PaginationRequest{
|
||||
Limit: 1,
|
||||
},
|
||||
Filters: []*metadata.UserByMetadataSearchFilter{
|
||||
{},
|
||||
},
|
||||
}
|
||||
model, err := ListUsersByMetadataRequestToModel(req, sysDefaults)
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, model)
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user