diff --git a/cmd/gitness/wire_gen.go b/cmd/gitness/wire_gen.go index 06d7bf73f..225295fb5 100644 --- a/cmd/gitness/wire_gen.go +++ b/cmd/gitness/wire_gen.go @@ -58,12 +58,12 @@ import ( // Injectors from wire.go: func initSystem(ctx context.Context, config *types.Config) (*server.System, error) { - principalUID := check.ProvidePrincipalUIDCheck() databaseConfig := server.ProvideDatabaseConfig(config) db, err := database.ProvideDatabase(ctx, databaseConfig) if err != nil { return nil, err } + principalUID := check.ProvidePrincipalUIDCheck() pathTransformation := store.ProvidePathTransformation() pathStore := database.ProvidePathStore(db, pathTransformation) pathCache := cache.ProvidePathCache(pathStore, pathTransformation) @@ -76,7 +76,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro principalUIDTransformation := store.ProvidePrincipalUIDTransformation() principalStore := database.ProvidePrincipalStore(db, principalUIDTransformation) tokenStore := database.ProvideTokenStore(db) - controller := user.NewController(principalUID, authorizer, principalStore, tokenStore, membershipStore) + controller := user.ProvideController(db, principalUID, authorizer, principalStore, tokenStore, membershipStore) serviceController := service.NewController(principalUID, authorizer, principalStore) bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller, serviceController) authenticator := authn.ProvideAuthenticator(principalStore, tokenStore) diff --git a/internal/api/controller/space/membership_list.go b/internal/api/controller/space/membership_list.go index 0ca6b460a..48de039c8 100644 --- a/internal/api/controller/space/membership_list.go +++ b/internal/api/controller/space/membership_list.go @@ -19,7 +19,7 @@ import ( func (c *Controller) MembershipList(ctx context.Context, session *auth.Session, spaceRef string, - opts types.MembershipFilter, + filter types.MembershipUserFilter, ) ([]types.MembershipUser, int64, error) { space, err := c.spaceStore.FindByRef(ctx, spaceRef) if err != nil { @@ -34,17 +34,17 @@ func (c *Controller) MembershipList(ctx context.Context, var membershipsCount int64 err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) error { - memberships, err = c.membershipStore.ListUsers(ctx, space.ID, opts) + memberships, err = c.membershipStore.ListUsers(ctx, space.ID, filter) if err != nil { return fmt.Errorf("failed to list memberships for space: %w", err) } - if opts.Page == 1 && len(memberships) < opts.Size { + if filter.Page == 1 && len(memberships) < filter.Size { membershipsCount = int64(len(memberships)) return nil } - membershipsCount, err = c.membershipStore.CountUsers(ctx, space.ID, opts) + membershipsCount, err = c.membershipStore.CountUsers(ctx, space.ID, filter) if err != nil { return fmt.Errorf("failed to count memberships for space: %w", err) } diff --git a/internal/api/controller/user/controller.go b/internal/api/controller/user/controller.go index 76060c3c1..5e3021367 100644 --- a/internal/api/controller/user/controller.go +++ b/internal/api/controller/user/controller.go @@ -13,10 +13,12 @@ import ( "github.com/harness/gitness/types/check" "github.com/harness/gitness/types/enum" + "github.com/jmoiron/sqlx" "golang.org/x/crypto/bcrypt" ) type Controller struct { + db *sqlx.DB principalUIDCheck check.PrincipalUID authorizer authz.Authorizer principalStore store.PrincipalStore @@ -25,6 +27,7 @@ type Controller struct { } func NewController( + db *sqlx.DB, principalUIDCheck check.PrincipalUID, authorizer authz.Authorizer, principalStore store.PrincipalStore, @@ -32,6 +35,7 @@ func NewController( membershipStore store.MembershipStore, ) *Controller { return &Controller{ + db: db, principalUIDCheck: principalUIDCheck, authorizer: authorizer, principalStore: principalStore, diff --git a/internal/api/controller/user/membership_spaces.go b/internal/api/controller/user/membership_spaces.go index a954e45d7..a9ecac4d5 100644 --- a/internal/api/controller/user/membership_spaces.go +++ b/internal/api/controller/user/membership_spaces.go @@ -10,6 +10,7 @@ import ( apiauth "github.com/harness/gitness/internal/api/auth" "github.com/harness/gitness/internal/auth" + "github.com/harness/gitness/store/database/dbtx" "github.com/harness/gitness/types" "github.com/harness/gitness/types/enum" ) @@ -18,21 +19,42 @@ import ( func (c *Controller) MembershipSpaces(ctx context.Context, session *auth.Session, userUID string, -) ([]types.MembershipSpace, error) { + filter types.MembershipSpaceFilter, +) ([]types.MembershipSpace, int64, error) { user, err := findUserFromUID(ctx, c.principalStore, userUID) if err != nil { - return nil, fmt.Errorf("failed to find user by UID: %w", err) + return nil, 0, fmt.Errorf("failed to find user by UID: %w", err) } // Ensure principal has required permissions. if err = apiauth.CheckUser(ctx, c.authorizer, session, user, enum.PermissionUserView); err != nil { - return nil, err + return nil, 0, err } - membershipSpaces, err := c.membershipStore.ListSpaces(ctx, user.ID) + var membershipSpaces []types.MembershipSpace + var membershipsCount int64 + + err = dbtx.New(c.db).WithTx(ctx, func(ctx context.Context) error { + membershipSpaces, err = c.membershipStore.ListSpaces(ctx, user.ID, filter) + if err != nil { + return fmt.Errorf("failed to list membership spaces for user: %w", err) + } + + if filter.Page == 1 && len(membershipSpaces) < filter.Size { + membershipsCount = int64(len(membershipSpaces)) + return nil + } + + membershipsCount, err = c.membershipStore.CountSpaces(ctx, user.ID, filter) + if err != nil { + return fmt.Errorf("failed to count memberships for user: %w", err) + } + + return nil + }, dbtx.TxDefaultReadOnly) if err != nil { - return nil, fmt.Errorf("failed to list membership spaces for user: %w", err) + return nil, 0, err } - return membershipSpaces, nil + return membershipSpaces, membershipsCount, nil } diff --git a/internal/api/controller/user/wire.go b/internal/api/controller/user/wire.go index bf8418dbf..7c1495c99 100644 --- a/internal/api/controller/user/wire.go +++ b/internal/api/controller/user/wire.go @@ -10,14 +10,16 @@ import ( "github.com/harness/gitness/types/check" "github.com/google/wire" + "github.com/jmoiron/sqlx" ) // WireSet provides a wire set for this package. var WireSet = wire.NewSet( - NewController, + ProvideController, ) func ProvideController( + db *sqlx.DB, principalUIDCheck check.PrincipalUID, authorizer authz.Authorizer, principalStore store.PrincipalStore, @@ -25,6 +27,7 @@ func ProvideController( membershipStore store.MembershipStore, ) *Controller { return NewController( + db, principalUIDCheck, authorizer, principalStore, diff --git a/internal/api/handler/space/membership_list.go b/internal/api/handler/space/membership_list.go index 715cd48d6..d93c76632 100644 --- a/internal/api/handler/space/membership_list.go +++ b/internal/api/handler/space/membership_list.go @@ -24,7 +24,7 @@ func HandleMembershipList(spaceCtrl *space.Controller) http.HandlerFunc { return } - filter := request.ParseMembershipFilter(r) + filter := request.ParseMembershipUserFilter(r) memberships, membershipsCount, err := spaceCtrl.MembershipList(ctx, session, spaceRef, filter) if err != nil { diff --git a/internal/api/handler/user/membership_spaces.go b/internal/api/handler/user/membership_spaces.go index b869f4f3d..2088549a8 100644 --- a/internal/api/handler/user/membership_spaces.go +++ b/internal/api/handler/user/membership_spaces.go @@ -18,12 +18,15 @@ func HandleMembershipSpaces(userCtrl *user.Controller) http.HandlerFunc { session, _ := request.AuthSessionFrom(ctx) userUID := session.Principal.UID - membershipSpaces, err := userCtrl.MembershipSpaces(ctx, session, userUID) + filter := request.ParseMembershipSpaceFilter(r) + + membershipSpaces, membershipSpaceCount, err := userCtrl.MembershipSpaces(ctx, session, userUID, filter) if err != nil { render.TranslatedUserError(w, err) return } + render.Pagination(r, w, filter.Page, filter.Size, int(membershipSpaceCount)) render.JSON(w, http.StatusOK, membershipSpaces) } } diff --git a/internal/api/openapi/space.go b/internal/api/openapi/space.go index bbbe1ed1d..cf63a9980 100644 --- a/internal/api/openapi/space.go +++ b/internal/api/openapi/space.go @@ -115,7 +115,7 @@ var queryParameterQuerySpace = openapi3.ParameterOrRef{ }, } -var queryParameterSpaceMembers = openapi3.ParameterOrRef{ +var queryParameterMembershipUsers = openapi3.ParameterOrRef{ Parameter: &openapi3.Parameter{ Name: request.QueryParamQuery, In: openapi3.ParameterInQuery, @@ -129,7 +129,7 @@ var queryParameterSpaceMembers = openapi3.ParameterOrRef{ }, } -var queryParameterSortSpaceMembers = openapi3.ParameterOrRef{ +var queryParameterSortMembershipUsers = openapi3.ParameterOrRef{ Parameter: &openapi3.Parameter{ Name: request.QueryParamSort, In: openapi3.ParameterInQuery, @@ -138,8 +138,8 @@ var queryParameterSortSpaceMembers = openapi3.ParameterOrRef{ Schema: &openapi3.SchemaOrRef{ Schema: &openapi3.Schema{ Type: ptrSchemaType(openapi3.SchemaTypeString), - Default: ptrptr(enum.MembershipSortName), - Enum: enum.MembershipSort("").Enum(), + Default: ptrptr(enum.MembershipUserSortName), + Enum: enum.MembershipUserSort("").Enum(), }, }, }, @@ -370,8 +370,8 @@ func spaceOperations(reflector *openapi3.Reflector) { opMembershipList.WithTags("space") opMembershipList.WithMapOfAnything(map[string]interface{}{"operationId": "membershipList"}) opMembershipList.WithParameters( - queryParameterSpaceMembers, - queryParameterOrder, queryParameterSortSpaceMembers, + queryParameterMembershipUsers, + queryParameterOrder, queryParameterSortMembershipUsers, queryParameterPage, queryParameterLimit) _ = reflector.SetRequest(&opMembershipList, &struct { spaceRequest diff --git a/internal/api/openapi/user.go b/internal/api/openapi/user.go index b7003efea..72fa022ff 100644 --- a/internal/api/openapi/user.go +++ b/internal/api/openapi/user.go @@ -8,9 +8,12 @@ import ( "net/http" "github.com/harness/gitness/internal/api/controller/user" + "github.com/harness/gitness/internal/api/request" "github.com/harness/gitness/internal/api/usererror" "github.com/harness/gitness/types" + "github.com/harness/gitness/types/enum" + "github.com/gotidy/ptr" "github.com/swaggest/openapi-go/openapi3" ) @@ -18,6 +21,36 @@ type createTokenRequest struct { user.CreateTokenInput } +var queryParameterMembershipSpaces = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamQuery, + In: openapi3.ParameterInQuery, + Description: ptr.String("The substring by which the spaces the users is a member of are filtered."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + }, + }, + }, +} + +var queryParameterSortMembershipSpaces = openapi3.ParameterOrRef{ + Parameter: &openapi3.Parameter{ + Name: request.QueryParamSort, + In: openapi3.ParameterInQuery, + Description: ptr.String("The field by which the spaces the user is a member of are sorted."), + Required: ptr.Bool(false), + Schema: &openapi3.SchemaOrRef{ + Schema: &openapi3.Schema{ + Type: ptrSchemaType(openapi3.SchemaTypeString), + Default: ptrptr(enum.MembershipSpaceSortUID), + Enum: enum.MembershipSpaceSort("").Enum(), + }, + }, + }, +} + // helper function that constructs the openapi specification // for user account resources. func buildUser(reflector *openapi3.Reflector) { @@ -48,6 +81,10 @@ func buildUser(reflector *openapi3.Reflector) { opMemberSpaces := openapi3.Operation{} opMemberSpaces.WithTags("user") opMemberSpaces.WithMapOfAnything(map[string]interface{}{"operationId": "membershipSpaces"}) + opMemberSpaces.WithParameters( + queryParameterMembershipSpaces, + queryParameterOrder, queryParameterSortMembershipSpaces, + queryParameterPage, queryParameterLimit) _ = reflector.SetRequest(&opMemberSpaces, struct{}{}, http.MethodGet) _ = reflector.SetJSONResponse(&opMemberSpaces, new([]types.MembershipSpace), http.StatusOK) _ = reflector.SetJSONResponse(&opMemberSpaces, new(usererror.Error), http.StatusInternalServerError) diff --git a/internal/api/request/membership.go b/internal/api/request/membership.go index 4aed70a67..e14ec9f86 100644 --- a/internal/api/request/membership.go +++ b/internal/api/request/membership.go @@ -11,20 +11,34 @@ import ( "github.com/harness/gitness/types/enum" ) -// ParseMembershipSort extracts the membership sort parameter from the url. -func ParseMembershipSort(r *http.Request) enum.MembershipSort { - return enum.ParseMembershipSort( +// ParseMembershipUserSort extracts the membership sort parameter from the url. +func ParseMembershipUserSort(r *http.Request) enum.MembershipUserSort { + return enum.ParseMembershipUserSort( r.URL.Query().Get(QueryParamSort), ) } -// ParseMembershipFilter extracts the membership filter from the url. -func ParseMembershipFilter(r *http.Request) types.MembershipFilter { - return types.MembershipFilter{ - Page: ParsePage(r), - Size: ParseLimit(r), - Query: ParseQuery(r), - Sort: ParseMembershipSort(r), - Order: ParseOrder(r), +// ParseMembershipUserFilter extracts the membership filter from the url. +func ParseMembershipUserFilter(r *http.Request) types.MembershipUserFilter { + return types.MembershipUserFilter{ + ListQueryFilter: ParseListQueryFilterFromRequest(r), + Sort: ParseMembershipUserSort(r), + Order: ParseOrder(r), + } +} + +// ParseMembershipSpaceSort extracts the membership space sort parameter from the url. +func ParseMembershipSpaceSort(r *http.Request) enum.MembershipSpaceSort { + return enum.ParseMembershipSpaceSort( + r.URL.Query().Get(QueryParamSort), + ) +} + +// ParseMembershipSpaceFilter extracts the membership space filter from the url. +func ParseMembershipSpaceFilter(r *http.Request) types.MembershipSpaceFilter { + return types.MembershipSpaceFilter{ + ListQueryFilter: ParseListQueryFilterFromRequest(r), + Sort: ParseMembershipSpaceSort(r), + Order: ParseOrder(r), } } diff --git a/internal/store/database.go b/internal/store/database.go index 634e9e6c2..c99bd0cba 100644 --- a/internal/store/database.go +++ b/internal/store/database.go @@ -230,9 +230,10 @@ type ( Create(ctx context.Context, membership *types.Membership) error Update(ctx context.Context, membership *types.Membership) error Delete(ctx context.Context, key types.MembershipKey) error - CountUsers(ctx context.Context, spaceID int64, filter types.MembershipFilter) (int64, error) - ListUsers(ctx context.Context, spaceID int64, filter types.MembershipFilter) ([]types.MembershipUser, error) - ListSpaces(ctx context.Context, userID int64) ([]types.MembershipSpace, error) + CountUsers(ctx context.Context, spaceID int64, filter types.MembershipUserFilter) (int64, error) + ListUsers(ctx context.Context, spaceID int64, filter types.MembershipUserFilter) ([]types.MembershipUser, error) + CountSpaces(ctx context.Context, userID int64, filter types.MembershipSpaceFilter) (int64, error) + ListSpaces(ctx context.Context, userID int64, filter types.MembershipSpaceFilter) ([]types.MembershipSpace, error) } // TokenStore defines the token data storage. diff --git a/internal/store/database/membership.go b/internal/store/database/membership.go index 3826612ad..6719445f1 100644 --- a/internal/store/database/membership.go +++ b/internal/store/database/membership.go @@ -183,7 +183,7 @@ func (s *MembershipStore) Delete(ctx context.Context, key types.MembershipKey) e // CountUsers returns a number of users memberships that matches the provided filter. func (s *MembershipStore) CountUsers(ctx context.Context, spaceID int64, - filter types.MembershipFilter, + filter types.MembershipUserFilter, ) (int64, error) { stmt := database.Builder. Select("count(*)"). @@ -191,11 +191,11 @@ func (s *MembershipStore) CountUsers(ctx context.Context, InnerJoin("principals ON membership_principal_id = principal_id"). Where("membership_space_id = ?", spaceID) - stmt = prepareMembershipListUsersStmt(stmt, filter) + stmt = applyMembershipUserFilter(stmt, filter) sql, args, err := stmt.ToSql() if err != nil { - return 0, fmt.Errorf("failed to convert membership count query to sql: %w", err) + return 0, fmt.Errorf("failed to convert membership users count query to sql: %w", err) } db := dbtx.GetAccessor(ctx, s.db) @@ -203,7 +203,7 @@ func (s *MembershipStore) CountUsers(ctx context.Context, var count int64 err = db.QueryRowContext(ctx, sql, args...).Scan(&count) if err != nil { - return 0, database.ProcessSQLErrorf(err, "Failed executing membership count query") + return 0, database.ProcessSQLErrorf(err, "Failed executing membership users count query") } return count, nil @@ -212,7 +212,7 @@ func (s *MembershipStore) CountUsers(ctx context.Context, // ListUsers returns a list of memberships for a space or a user. func (s *MembershipStore) ListUsers(ctx context.Context, spaceID int64, - filter types.MembershipFilter, + filter types.MembershipUserFilter, ) ([]types.MembershipUser, error) { const columns = membershipColumns + "," + principalInfoCommonColumns stmt := database.Builder. @@ -221,7 +221,7 @@ func (s *MembershipStore) ListUsers(ctx context.Context, InnerJoin("principals ON membership_principal_id = principal_id"). Where("membership_space_id = ?", spaceID) - stmt = prepareMembershipListUsersStmt(stmt, filter) + stmt = applyMembershipUserFilter(stmt, filter) stmt = stmt.Limit(database.Limit(filter.Size)) stmt = stmt.Offset(database.Offset(filter.Page, filter.Size)) @@ -231,9 +231,9 @@ func (s *MembershipStore) ListUsers(ctx context.Context, } switch filter.Sort { - case enum.MembershipSortName: + case enum.MembershipUserSortName: stmt = stmt.OrderBy("principal_display_name " + order.String()) - case enum.MembershipSortCreated: + case enum.MembershipUserSortCreated: stmt = stmt.OrderBy("membership_created " + order.String()) } @@ -258,9 +258,9 @@ func (s *MembershipStore) ListUsers(ctx context.Context, return result, nil } -func prepareMembershipListUsersStmt( +func applyMembershipUserFilter( stmt squirrel.SelectBuilder, - opts types.MembershipFilter, + opts types.MembershipUserFilter, ) squirrel.SelectBuilder { if opts.Query != "" { searchTerm := "%%" + strings.ToLower(opts.Query) + "%%" @@ -270,9 +270,38 @@ func prepareMembershipListUsersStmt( return stmt } +func (s *MembershipStore) CountSpaces(ctx context.Context, + userID int64, + filter types.MembershipSpaceFilter, +) (int64, error) { + stmt := database.Builder. + Select("count(*)"). + From("memberships"). + InnerJoin("spaces ON spaces.space_id = membership_space_id"). + Where("membership_principal_id = ?", userID) + + stmt = applyMembershipSpaceFilter(stmt, filter) + + sql, args, err := stmt.ToSql() + if err != nil { + return 0, fmt.Errorf("failed to convert membership spaces count query to sql: %w", err) + } + + db := dbtx.GetAccessor(ctx, s.db) + + var count int64 + err = db.QueryRowContext(ctx, sql, args...).Scan(&count) + if err != nil { + return 0, database.ProcessSQLErrorf(err, "Failed executing membership spaces count query") + } + + return count, nil +} + // ListSpaces returns a list of spaces in which the provided user is a member. func (s *MembershipStore) ListSpaces(ctx context.Context, userID int64, + filter types.MembershipSpaceFilter, ) ([]types.MembershipSpace, error) { const columns = membershipColumns + "," + spaceColumnsForJoin stmt := database.Builder. @@ -280,8 +309,25 @@ func (s *MembershipStore) ListSpaces(ctx context.Context, From("memberships"). InnerJoin("spaces ON spaces.space_id = membership_space_id"). InnerJoin(`paths ON spaces.space_id=paths.path_space_id AND paths.path_is_primary=true`). - Where("membership_principal_id = ?", userID). - OrderBy("space_path asc") + Where("membership_principal_id = ?", userID) + + stmt = applyMembershipSpaceFilter(stmt, filter) + stmt = stmt.Limit(database.Limit(filter.Size)) + stmt = stmt.Offset(database.Offset(filter.Page, filter.Size)) + + order := filter.Order + if order == enum.OrderDefault { + order = enum.OrderAsc + } + + switch filter.Sort { + case enum.MembershipSpaceSortUID: + stmt = stmt.OrderBy("space_uid " + order.String()) + case enum.MembershipSpaceSortPath: + stmt = stmt.OrderBy("space_path " + order.String()) + case enum.MembershipSpaceSortCreated: + stmt = stmt.OrderBy("membership_created " + order.String()) + } sql, args, err := stmt.ToSql() if err != nil { @@ -303,6 +349,18 @@ func (s *MembershipStore) ListSpaces(ctx context.Context, return result, nil } +func applyMembershipSpaceFilter( + stmt squirrel.SelectBuilder, + opts types.MembershipSpaceFilter, +) squirrel.SelectBuilder { + if opts.Query != "" { + searchTerm := "%%" + strings.ToLower(opts.Query) + "%%" + stmt = stmt.Where("LOWER(space_uid) LIKE ?", searchTerm) + } + + return stmt +} + func mapToMembership(m *membership) types.Membership { return types.Membership{ MembershipKey: types.MembershipKey{ diff --git a/types/enum/membership.go b/types/enum/membership.go index 60e831900..fb47d732b 100644 --- a/types/enum/membership.go +++ b/types/enum/membership.go @@ -8,45 +8,100 @@ import ( "strings" ) -// MembershipSort represents membership sort order. -type MembershipSort string +// MembershipUserSort represents membership user sort order. +type MembershipUserSort string -// Order enumeration. +// MembershipUserSort enumeration. const ( - MembershipSortName = name - MembershipSortCreated = created + MembershipUserSortName MembershipUserSort = name + MembershipUserSortCreated MembershipUserSort = created ) -var membershipSorts = sortEnum([]MembershipSort{ - MembershipSortName, - MembershipSortCreated, +var membershipUserSorts = sortEnum([]MembershipUserSort{ + MembershipUserSortName, + MembershipUserSortCreated, }) -func (MembershipSort) Enum() []interface{} { return toInterfaceSlice(membershipSorts) } -func (s MembershipSort) Sanitize() (MembershipSort, bool) { return Sanitize(s, GetAllMembershipSorts) } -func GetAllMembershipSorts() ([]MembershipSort, MembershipSort) { - return membershipSorts, MembershipSortName +func (MembershipUserSort) Enum() []interface{} { return toInterfaceSlice(membershipUserSorts) } +func (s MembershipUserSort) Sanitize() (MembershipUserSort, bool) { + return Sanitize(s, GetAllMembershipUserSorts) +} +func GetAllMembershipUserSorts() ([]MembershipUserSort, MembershipUserSort) { + return membershipUserSorts, MembershipUserSortName } -// ParseMembershipSort parses the membership sort attribute string +// ParseMembershipUserSort parses the membership user sort attribute string // and returns the equivalent enumeration. -func ParseMembershipSort(s string) MembershipSort { +func ParseMembershipUserSort(s string) MembershipUserSort { switch strings.ToLower(s) { case name: - return MembershipSortName + return MembershipUserSortName case created, createdAt: - return MembershipSortCreated + return MembershipUserSortCreated default: - return MembershipSortName + return MembershipUserSortName } } // String returns the string representation of the attribute. -func (s MembershipSort) String() string { +func (s MembershipUserSort) String() string { switch s { - case MembershipSortName: + case MembershipUserSortName: return name - case MembershipSortCreated: + case MembershipUserSortCreated: + return created + default: + return undefined + } +} + +// MembershipSpaceSort represents membership space sort order. +type MembershipSpaceSort string + +// MembershipSpaceSort enumeration. +const ( + MembershipSpaceSortUID MembershipSpaceSort = uid + MembershipSpaceSortPath MembershipSpaceSort = path + MembershipSpaceSortCreated MembershipSpaceSort = created +) + +var membershipSpaceSorts = sortEnum([]MembershipSpaceSort{ + MembershipSpaceSortUID, + MembershipSpaceSortPath, + MembershipSpaceSortCreated, +}) + +func (MembershipSpaceSort) Enum() []interface{} { return toInterfaceSlice(membershipSpaceSorts) } +func (s MembershipSpaceSort) Sanitize() (MembershipSpaceSort, bool) { + return Sanitize(s, GetAllMembershipSpaceSorts) +} +func GetAllMembershipSpaceSorts() ([]MembershipSpaceSort, MembershipSpaceSort) { + return membershipSpaceSorts, MembershipSpaceSortPath +} + +// ParseMembershipSpaceSort parses the membership space sort attribute string +// and returns the equivalent enumeration. +func ParseMembershipSpaceSort(s string) MembershipSpaceSort { + switch strings.ToLower(s) { + case name: + return MembershipSpaceSortUID + case path: + return MembershipSpaceSortPath + case created, createdAt: + return MembershipSpaceSortCreated + default: + return MembershipSpaceSortUID + } +} + +// String returns the string representation of the attribute. +func (s MembershipSpaceSort) String() string { + switch s { + case MembershipSpaceSortUID: + return uid + case MembershipSpaceSortPath: + return path + case MembershipSpaceSortCreated: return created default: return undefined diff --git a/types/membership.go b/types/membership.go index e8a908c23..d5b64ef5b 100644 --- a/types/membership.go +++ b/types/membership.go @@ -32,6 +32,13 @@ type MembershipUser struct { AddedBy PrincipalInfo `json:"added_by"` } +// MembershipUserFilter holds membership user query parameters. +type MembershipUserFilter struct { + ListQueryFilter + Sort enum.MembershipUserSort `json:"sort"` + Order enum.Order `json:"order"` +} + // MembershipSpace adds space info to the Membership data. type MembershipSpace struct { Membership @@ -39,11 +46,9 @@ type MembershipSpace struct { AddedBy PrincipalInfo `json:"added_by"` } -// MembershipFilter holds membership query parameters. -type MembershipFilter struct { - Page int `json:"page"` - Size int `json:"size"` - Query string `json:"query"` - Sort enum.MembershipSort `json:"sort"` - Order enum.Order `json:"order"` +// MembershipSpaceFilter holds membership space query parameters. +type MembershipSpaceFilter struct { + ListQueryFilter + Sort enum.MembershipSpaceSort `json:"sort"` + Order enum.Order `json:"order"` }