mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Team: Add sub resource and api for team members (#92492)
* Add team members as a sub resource * Fix and clean up pagination for teams * Fix and clean up pagination for users * Fix and clean up pagination for service accounts * Update snapshots
This commit is contained in:
parent
72be3e861e
commit
4addd9637e
@ -170,6 +170,7 @@ func AddKnownTypes(scheme *runtime.Scheme, version string) {
|
||||
&SSOSettingList{},
|
||||
&TeamBinding{},
|
||||
&TeamBindingList{},
|
||||
&TeamMemberList{},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -23,11 +23,19 @@ type IdentityDisplayResults struct {
|
||||
}
|
||||
|
||||
type IdentityDisplay struct {
|
||||
IdentityType claims.IdentityType `json:"type"` // The namespaced UID, eg `user|api-key|...`
|
||||
UID string `json:"uid"` // The namespaced UID, eg `xyz`
|
||||
Display string `json:"display"`
|
||||
AvatarURL string `json:"avatarURL,omitempty"`
|
||||
// Type of identity e.g. "user".
|
||||
// For a full list see https://github.com/grafana/authlib/blob/2f8d13a83ca3e82da08b53726de1697ee5b5b4cc/claims/type.go#L15-L24
|
||||
IdentityType claims.IdentityType `json:"type"`
|
||||
|
||||
// Legacy internal ID -- usage of this value should be phased out
|
||||
// UID for identity, is a unique value for the type within a namespace.
|
||||
UID string `json:"uid"`
|
||||
|
||||
// Display name for identity.
|
||||
Display string `json:"display"`
|
||||
|
||||
// AvatarURL is the url where we can get the avatar for identity
|
||||
AvatarURL string `json:"avatarURL,omitempty"`
|
||||
|
||||
// InternalID is the legacy numreric id for identity, this is deprecated and should be phased out
|
||||
InternalID int64 `json:"internalId,omitempty"`
|
||||
}
|
||||
|
@ -50,8 +50,7 @@ type TeamSubject struct {
|
||||
// Name is the unique identifier for subject.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Permission subject has in permission.
|
||||
// Can be either admin or member.
|
||||
// Permission subject has in team.
|
||||
Permission TeamPermission `json:"permission,omitempty"`
|
||||
}
|
||||
|
||||
@ -60,6 +59,23 @@ type TeamRef struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type TeamMemberList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []TeamMember `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
type TeamMember struct {
|
||||
IdentityDisplay `json:",inline"`
|
||||
|
||||
// External is set if member ship was synced from external IDP.
|
||||
External bool `json:"external,omitempty"`
|
||||
// Permission member has in team.
|
||||
Permission TeamPermission `json:"permission,omitempty"`
|
||||
}
|
||||
|
||||
// TeamPermission for subject
|
||||
// +enum
|
||||
type TeamPermission string
|
||||
|
@ -362,6 +362,54 @@ func (in *TeamList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamMember) DeepCopyInto(out *TeamMember) {
|
||||
*out = *in
|
||||
out.IdentityDisplay = in.IdentityDisplay
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamMember.
|
||||
func (in *TeamMember) DeepCopy() *TeamMember {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TeamMember)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamMemberList) DeepCopyInto(out *TeamMemberList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]TeamMember, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamMemberList.
|
||||
func (in *TeamMemberList) DeepCopy() *TeamMemberList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TeamMemberList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TeamMemberList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamRef) DeepCopyInto(out *TeamRef) {
|
||||
*out = *in
|
||||
|
@ -27,6 +27,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBindingList": schema_pkg_apis_identity_v0alpha1_TeamBindingList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBindingSpec": schema_pkg_apis_identity_v0alpha1_TeamBindingSpec(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamList": schema_pkg_apis_identity_v0alpha1_TeamList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamMember": schema_pkg_apis_identity_v0alpha1_TeamMember(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamMemberList": schema_pkg_apis_identity_v0alpha1_TeamMemberList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamRef": schema_pkg_apis_identity_v0alpha1_TeamRef(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamSpec": schema_pkg_apis_identity_v0alpha1_TeamSpec(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamSubject": schema_pkg_apis_identity_v0alpha1_TeamSubject(ref),
|
||||
@ -44,14 +46,15 @@ func schema_pkg_apis_identity_v0alpha1_IdentityDisplay(ref common.ReferenceCallb
|
||||
Properties: map[string]spec.Schema{
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Description: "Type of identity e.g. \"user\". For a full list see https://github.com/grafana/authlib/blob/2f8d13a83ca3e82da08b53726de1697ee5b5b4cc/claims/type.go#L15-L24",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"uid": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The namespaced UID, eg `user|api-key|...`",
|
||||
Description: "UID for identity, is a unique value for the type within a namespace.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
@ -59,7 +62,7 @@ func schema_pkg_apis_identity_v0alpha1_IdentityDisplay(ref common.ReferenceCallb
|
||||
},
|
||||
"display": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The namespaced UID, eg `xyz`",
|
||||
Description: "Display name for identity.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
@ -67,13 +70,14 @@ func schema_pkg_apis_identity_v0alpha1_IdentityDisplay(ref common.ReferenceCallb
|
||||
},
|
||||
"avatarURL": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Description: "AvatarURL is the url where we can get the avatar for identity",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"internalId": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Legacy internal ID -- usage of this value should be phased out",
|
||||
Description: "InternalID is the legacy numreric id for identity, this is deprecated and should be phased out",
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
@ -621,6 +625,119 @@ func schema_pkg_apis_identity_v0alpha1_TeamList(ref common.ReferenceCallback) co
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamMember(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Type of identity e.g. \"user\". For a full list see https://github.com/grafana/authlib/blob/2f8d13a83ca3e82da08b53726de1697ee5b5b4cc/claims/type.go#L15-L24",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"uid": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "UID for identity, is a unique value for the type within a namespace.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"display": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Display name for identity.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"avatarURL": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "AvatarURL is the url where we can get the avatar for identity",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"internalId": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "InternalID is the legacy numreric id for identity, this is deprecated and should be phased out",
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"external": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "External is set if member ship was synced from external IDP.",
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"permission": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Permission member has in team.\n\nPossible enum values:\n - `\"admin\"`\n - `\"member\"`",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Enum: []interface{}{"admin", "member"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"type", "uid", "display"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamMemberList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamMember"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamMember", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamRef(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@ -679,7 +796,7 @@ func schema_pkg_apis_identity_v0alpha1_TeamSubject(ref common.ReferenceCallback)
|
||||
},
|
||||
"permission": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Permission subject has in permission. Can be either admin or member.\n\nPossible enum values:\n - `\"admin\"`\n - `\"member\"`",
|
||||
Description: "Permission subject has in team.\n\nPossible enum values:\n - `\"admin\"`\n - `\"member\"`",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Enum: []interface{}{"admin", "member"},
|
||||
|
@ -53,6 +53,7 @@ func (r *subAccessREST) Connect(ctx context.Context, name string, opts runtime.O
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Can view is managed here (and in the Authorizer)
|
||||
f, err := r.service.Get(ctx, &folder.GetFolderQuery{
|
||||
UID: &name,
|
||||
|
@ -1,25 +1,9 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
)
|
||||
|
||||
// GetContinueID is a helper to parse options.Continue as int64.
|
||||
// If no continue token is provided 0 is returned.
|
||||
func GetContinueID(options *internalversion.ListOptions) (int64, error) {
|
||||
if options.Continue != "" {
|
||||
continueID, err := strconv.ParseInt(options.Continue, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid continue token: %w", err)
|
||||
}
|
||||
return continueID, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// OptonalFormatInt formats num as a string. If num is less or equal than 0
|
||||
// an empty string is returned.
|
||||
func OptionalFormatInt(num int64) string {
|
||||
|
48
pkg/registry/apis/identity/common/pagination.go
Normal file
48
pkg/registry/apis/identity/common/pagination.go
Normal file
@ -0,0 +1,48 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
)
|
||||
|
||||
type Pagination struct {
|
||||
Limit int64
|
||||
Continue int64
|
||||
}
|
||||
|
||||
func PaginationFromListOptions(options *internalversion.ListOptions) Pagination {
|
||||
limit := options.Limit
|
||||
if limit < 1 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
return Pagination{
|
||||
Limit: limit,
|
||||
Continue: parseIntWithFallback(options.Continue, 0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func PaginationFromListQuery(query url.Values) Pagination {
|
||||
return Pagination{
|
||||
Limit: parseIntWithFallback(query.Get("limit"), 1, 50),
|
||||
Continue: parseIntWithFallback(query.Get("continue"), 0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func parseIntWithFallback(original string, min int64, fallback int64) int64 {
|
||||
if original == "" {
|
||||
return fallback
|
||||
}
|
||||
v, err := strconv.ParseInt(original, 10, 64)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
if v < min {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
@ -2,6 +2,7 @@ package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"text/template"
|
||||
|
||||
@ -28,12 +29,8 @@ type legacySQLStore struct {
|
||||
|
||||
// ListTeams implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error) {
|
||||
if query.Limit < 1 {
|
||||
query.Limit = 50
|
||||
}
|
||||
|
||||
limit := int(query.Limit)
|
||||
query.Limit += 1 // for continue
|
||||
// for continue
|
||||
query.Pagination.Limit += 1
|
||||
query.OrgID = ns.OrgID
|
||||
if ns.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero orgID")
|
||||
@ -45,13 +42,11 @@ func (s *legacySQLStore) ListTeams(ctx context.Context, ns claims.NamespaceInfo,
|
||||
}
|
||||
|
||||
req := newListTeams(sql, &query)
|
||||
rawQuery, err := sqltemplate.Execute(sqlQueryTeams, req)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeams, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeams.Name(), err)
|
||||
}
|
||||
q := rawQuery
|
||||
|
||||
res := &ListTeamResult{}
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
@ -59,37 +54,41 @@ func (s *legacySQLStore) ListTeams(ctx context.Context, ns claims.NamespaceInfo,
|
||||
}
|
||||
}()
|
||||
|
||||
if err == nil {
|
||||
// id, uid, name, email, created, updated
|
||||
var lastID int64
|
||||
for rows.Next() {
|
||||
t := team.Team{}
|
||||
err = rows.Scan(&t.ID, &t.UID, &t.Name, &t.Email, &t.Created, &t.Updated)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
lastID = t.ID
|
||||
res.Teams = append(res.Teams, t)
|
||||
if len(res.Teams) > limit {
|
||||
res.ContinueID = lastID
|
||||
break
|
||||
}
|
||||
res := &ListTeamResult{}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var lastID int64
|
||||
for rows.Next() {
|
||||
t := team.Team{}
|
||||
err = rows.Scan(&t.ID, &t.UID, &t.Name, &t.Email, &t.Created, &t.Updated)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if query.UID == "" {
|
||||
res.RV, err = sql.GetResourceVersion(ctx, "team", "updated")
|
||||
|
||||
lastID = t.ID
|
||||
res.Teams = append(res.Teams, t)
|
||||
if len(res.Teams) > int(query.Pagination.Limit)-1 {
|
||||
res.Teams = res.Teams[0 : len(res.Teams)-1]
|
||||
res.Continue = lastID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if query.UID == "" {
|
||||
res.RV, err = sql.GetResourceVersion(ctx, "team", "updated")
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ListUsers implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error) {
|
||||
if query.Limit < 1 {
|
||||
query.Limit = 50
|
||||
}
|
||||
// for continue
|
||||
limit := int(query.Pagination.Limit)
|
||||
query.Pagination.Limit += 1
|
||||
|
||||
limit := int(query.Limit)
|
||||
query.Limit += 1 // for continue
|
||||
query.OrgID = ns.OrgID
|
||||
if ns.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero orgID")
|
||||
@ -101,10 +100,10 @@ func (s *legacySQLStore) ListUsers(ctx context.Context, ns claims.NamespaceInfo,
|
||||
}
|
||||
|
||||
res, err := s.queryUsers(ctx, sql, sqlQueryUsers, newListUser(sql, &query), limit)
|
||||
|
||||
if err == nil && query.UID != "" {
|
||||
res.RV, err = sql.GetResourceVersion(ctx, "user", "updated")
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
@ -147,25 +146,24 @@ func (s *legacySQLStore) queryUsers(ctx context.Context, sql *legacysql.LegacyDa
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
lastID = u.ID
|
||||
res.Users = append(res.Users, u)
|
||||
if len(res.Users) > limit {
|
||||
res.ContinueID = lastID
|
||||
res.Users = res.Users[0 : len(res.Users)-1]
|
||||
res.Continue = lastID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ListTeamsBindings implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error) {
|
||||
if query.Limit < 1 {
|
||||
query.Limit = 50
|
||||
}
|
||||
|
||||
limit := int(query.Limit)
|
||||
query.Limit += 1 // for continue
|
||||
// for continue
|
||||
query.Pagination.Limit += 1
|
||||
query.OrgID = ns.OrgID
|
||||
if query.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero orgID")
|
||||
@ -214,9 +212,9 @@ func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.Namespa
|
||||
grouped[m.TeamUID] = []TeamMember{m}
|
||||
}
|
||||
|
||||
if len(grouped) >= limit {
|
||||
if len(grouped) >= int(query.Pagination.Limit)-1 {
|
||||
atTeamLimit = true
|
||||
res.ContinueID = lastID
|
||||
res.Continue = lastID
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,6 +233,62 @@ func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.Namespa
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ListTeamMembers implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error) {
|
||||
query.Pagination.Limit += 1
|
||||
query.OrgID = ns.OrgID
|
||||
if query.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero org id")
|
||||
}
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := newListTeamMembers(sql, &query)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeamMembers, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeams.Name(), err)
|
||||
}
|
||||
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
_ = rows.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &ListTeamMembersResult{}
|
||||
var lastID int64
|
||||
for rows.Next() {
|
||||
m, err := scanMember(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lastID = m.ID
|
||||
res.Members = append(res.Members, m)
|
||||
if len(res.Members) > int(query.Pagination.Limit)-1 {
|
||||
res.Continue = lastID
|
||||
res.Members = res.Members[0 : len(res.Members)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
func scanMember(rows *sql.Rows) (TeamMember, error) {
|
||||
m := TeamMember{}
|
||||
err := rows.Scan(&m.ID, &m.TeamUID, &m.TeamID, &m.UserUID, &m.UserID, &m.Name, &m.Email, &m.Username, &m.External, &m.Created, &m.Updated, &m.Permission)
|
||||
return m, err
|
||||
}
|
||||
|
||||
// GetUserTeams implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) GetUserTeams(ctx context.Context, ns claims.NamespaceInfo, uid string) ([]team.Team, error) {
|
||||
panic("unimplemented")
|
||||
|
@ -30,6 +30,7 @@ var (
|
||||
sqlQueryUsers = mustTemplate("query_users.sql")
|
||||
sqlQueryDisplay = mustTemplate("query_display.sql")
|
||||
sqlQueryTeamBindings = mustTemplate("query_team_bindings.sql")
|
||||
sqlQueryTeamMembers = mustTemplate("query_team_members.sql")
|
||||
)
|
||||
|
||||
type sqlQueryListUsers struct {
|
||||
@ -111,3 +112,25 @@ func newListTeamBindings(sql *legacysql.LegacyDatabaseHelper, q *ListTeamBinding
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
type sqlQueryListTeamMembers struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamMembersQuery
|
||||
UserTable string
|
||||
TeamTable string
|
||||
TeamMemberTable string
|
||||
}
|
||||
|
||||
func (r sqlQueryListTeamMembers) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func newListTeamMembers(sql *legacysql.LegacyDatabaseHelper, q *ListTeamMembersQuery) sqlQueryListTeamMembers {
|
||||
return sqlQueryListTeamMembers{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
TeamTable: sql.Table("team"),
|
||||
TeamMemberTable: sql.Table("team_member"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/sql/sqltemplate/mocks"
|
||||
@ -41,6 +42,12 @@ func TestIdentityQueries(t *testing.T) {
|
||||
return &v
|
||||
}
|
||||
|
||||
listTeamMembers := func(q *ListTeamMembersQuery) sqltemplate.SQLTemplate {
|
||||
v := newListTeamMembers(nodb, q)
|
||||
v.SQLTemplate = mocks.NewTestingSQLTemplate()
|
||||
return &v
|
||||
}
|
||||
|
||||
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
|
||||
RootDir: "testdata",
|
||||
Templates: map[*template.Template][]mocks.TemplateTestCase{
|
||||
@ -48,20 +55,23 @@ func TestIdentityQueries(t *testing.T) {
|
||||
{
|
||||
Name: "teams_uid",
|
||||
Data: listTeams(&ListTeamQuery{
|
||||
UID: "abc",
|
||||
UID: "abc",
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "teams_page_1",
|
||||
Data: listTeams(&ListTeamQuery{
|
||||
Limit: 5,
|
||||
Pagination: common.Pagination{Limit: 5},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "teams_page_2",
|
||||
Data: listTeams(&ListTeamQuery{
|
||||
ContinueID: 1,
|
||||
Limit: 2,
|
||||
Pagination: common.Pagination{
|
||||
Limit: 1,
|
||||
Continue: 2,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -69,20 +79,23 @@ func TestIdentityQueries(t *testing.T) {
|
||||
{
|
||||
Name: "users_uid",
|
||||
Data: listUsers(&ListUserQuery{
|
||||
UID: "abc",
|
||||
UID: "abc",
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "users_page_1",
|
||||
Data: listUsers(&ListUserQuery{
|
||||
Limit: 5,
|
||||
Pagination: common.Pagination{Limit: 5},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "users_page_2",
|
||||
Data: listUsers(&ListUserQuery{
|
||||
ContinueID: 1,
|
||||
Limit: 2,
|
||||
Pagination: common.Pagination{
|
||||
Limit: 1,
|
||||
Continue: 2,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
@ -114,23 +127,44 @@ func TestIdentityQueries(t *testing.T) {
|
||||
{
|
||||
Name: "team_1_bindings",
|
||||
Data: listTeamBindings(&ListTeamBindingsQuery{
|
||||
OrgID: 1,
|
||||
UID: "team-1",
|
||||
OrgID: 1,
|
||||
UID: "team-1",
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "team_bindings_page_1",
|
||||
Data: listTeamBindings(&ListTeamBindingsQuery{
|
||||
OrgID: 1,
|
||||
Limit: 5,
|
||||
OrgID: 1,
|
||||
Pagination: common.Pagination{Limit: 5},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "team_bindings_page_2",
|
||||
Data: listTeamBindings(&ListTeamBindingsQuery{
|
||||
OrgID: 1,
|
||||
Pagination: common.Pagination{
|
||||
Limit: 1,
|
||||
Continue: 2,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlQueryTeamMembers: {
|
||||
{
|
||||
Name: "team_1_members_page_1",
|
||||
Data: listTeamMembers(&ListTeamMembersQuery{
|
||||
UID: "team-1",
|
||||
OrgID: 1,
|
||||
Limit: 5,
|
||||
ContinueID: 5,
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "team_1_members_page_2",
|
||||
Data: listTeamMembers(&ListTeamMembersQuery{
|
||||
UID: "team-1",
|
||||
OrgID: 1,
|
||||
Pagination: common.Pagination{Limit: 1, Continue: 2},
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
@ -8,11 +8,12 @@ WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM {{ .Ident .TeamTable }} t
|
||||
{{ if .Query.ContinueID }}
|
||||
WHERE t.id >= {{ .Arg .Query.ContinueID }}
|
||||
{{ if .Query.Pagination.Continue }}
|
||||
WHERE t.id >= {{ .Arg .Query.Pagination.Continue }}
|
||||
{{ end }}
|
||||
ORDER BY t.id ASC LIMIT {{ .Arg .Query.Limit }}
|
||||
ORDER BY t.id ASC LIMIT {{ .Arg .Query.Pagination.Limit }}
|
||||
)
|
||||
{{ end }}
|
||||
AND tm.org_id = {{ .Arg .Query.OrgID}}
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
24
pkg/registry/apis/identity/legacy/query_team_members.sql
Normal file
24
pkg/registry/apis/identity/legacy/query_team_members.sql
Normal file
@ -0,0 +1,24 @@
|
||||
SELECT
|
||||
tm.id as id,
|
||||
t.uid as team_uid,
|
||||
t.id as team_id,
|
||||
u.uid as user_uid,
|
||||
u.id as user_id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.login,
|
||||
tm.external,
|
||||
tm.created,
|
||||
tm.updated,
|
||||
tm.permission
|
||||
FROM {{ .Ident .TeamMemberTable }} tm
|
||||
INNER JOIN {{ .Ident .TeamTable }} t ON tm.team_id = t.id
|
||||
INNER JOIN {{ .Ident .UserTable }} u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = {{ .Arg .Query.UID }}
|
||||
{{- if .Query.Pagination.Continue }}
|
||||
AND tm.id >= {{ .Arg .Query.Pagination.Continue }}
|
||||
{{- end }}
|
||||
AND tm.org_id = {{ .Arg .Query.OrgID }}
|
||||
ORDER BY t.id ASC
|
||||
LIMIT {{ .Arg .Query.Pagination.Limit }};
|
@ -4,8 +4,8 @@ SELECT id, uid, name, email, created, updated
|
||||
{{ if .Query.UID }}
|
||||
AND uid = {{ .Arg .Query.UID }}
|
||||
{{ end }}
|
||||
{{ if .Query.ContinueID }}
|
||||
AND id > {{ .Arg .Query.ContinueID }}
|
||||
{{ if .Query.Pagination.Continue }}
|
||||
AND id >= {{ .Arg .Query.Pagination.Continue }}
|
||||
{{ end }}
|
||||
ORDER BY id asc
|
||||
LIMIT {{ .Arg .Query.Limit }}
|
||||
LIMIT {{ .Arg .Query.Pagination.Limit }}
|
||||
|
@ -4,10 +4,10 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
WHERE o.org_id = {{ .Arg .Query.OrgID }}
|
||||
AND u.is_service_account = {{ .Arg .Query.IsServiceAccount }}
|
||||
{{ if .Query.UID }}
|
||||
AND uid = {{ .Arg .Query.UID }}
|
||||
AND u.uid = {{ .Arg .Query.UID }}
|
||||
{{ end }}
|
||||
{{ if .Query.ContinueID }}
|
||||
AND id > {{ .Arg .Query.ContinueID }}
|
||||
{{ if .Query.Pagination.Continue }}
|
||||
AND u.id >= {{ .Arg .Query.Pagination.Continue }}
|
||||
{{ end }}
|
||||
ORDER BY u.id asc
|
||||
LIMIT {{ .Arg .Query.Limit }}
|
||||
LIMIT {{ .Arg .Query.Pagination.Limit }}
|
||||
|
@ -4,4 +4,5 @@ INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
@ -8,4 +8,5 @@ WHERE
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
@ -5,8 +5,9 @@ WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM `grafana`.`team` t
|
||||
WHERE t.id >= 5
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
WHERE t.id >= 2
|
||||
ORDER BY t.id ASC LIMIT 1
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
21
pkg/registry/apis/identity/legacy/testdata/mysql--query_team_members-team_1_members_page_1.sql
vendored
Executable file
21
pkg/registry/apis/identity/legacy/testdata/mysql--query_team_members-team_1_members_page_1.sql
vendored
Executable file
@ -0,0 +1,21 @@
|
||||
SELECT
|
||||
tm.id as id,
|
||||
t.uid as team_uid,
|
||||
t.id as team_id,
|
||||
u.uid as user_uid,
|
||||
u.id as user_id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.login,
|
||||
tm.external,
|
||||
tm.created,
|
||||
tm.updated,
|
||||
tm.permission
|
||||
FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
22
pkg/registry/apis/identity/legacy/testdata/mysql--query_team_members-team_1_members_page_2.sql
vendored
Executable file
22
pkg/registry/apis/identity/legacy/testdata/mysql--query_team_members-team_1_members_page_2.sql
vendored
Executable file
@ -0,0 +1,22 @@
|
||||
SELECT
|
||||
tm.id as id,
|
||||
t.uid as team_uid,
|
||||
t.id as team_id,
|
||||
u.uid as user_uid,
|
||||
u.id as user_id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.login,
|
||||
tm.external,
|
||||
tm.created,
|
||||
tm.updated,
|
||||
tm.permission
|
||||
FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.id >= 2
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
@ -1,6 +1,6 @@
|
||||
SELECT id, uid, name, email, created, updated
|
||||
FROM `grafana`.`team`
|
||||
WHERE org_id = 0
|
||||
AND id > 1
|
||||
AND id >= 2
|
||||
ORDER BY id asc
|
||||
LIMIT 2
|
||||
LIMIT 1
|
||||
|
@ -3,4 +3,4 @@ SELECT id, uid, name, email, created, updated
|
||||
WHERE org_id = 0
|
||||
AND uid = 'abc'
|
||||
ORDER BY id asc
|
||||
LIMIT 0
|
||||
LIMIT 1
|
||||
|
@ -3,6 +3,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND u.is_service_account = FALSE
|
||||
AND id > 1
|
||||
AND u.id >= 2
|
||||
ORDER BY u.id asc
|
||||
LIMIT 2
|
||||
LIMIT 1
|
||||
|
@ -3,6 +3,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND u.is_service_account = FALSE
|
||||
AND uid = 'abc'
|
||||
AND u.uid = 'abc'
|
||||
ORDER BY u.id asc
|
||||
LIMIT 0
|
||||
LIMIT 1
|
||||
|
@ -4,4 +4,5 @@ INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
@ -8,4 +8,5 @@ WHERE
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
@ -5,8 +5,9 @@ WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM "grafana"."team" t
|
||||
WHERE t.id >= 5
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
WHERE t.id >= 2
|
||||
ORDER BY t.id ASC LIMIT 1
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
21
pkg/registry/apis/identity/legacy/testdata/postgres--query_team_members-team_1_members_page_1.sql
vendored
Executable file
21
pkg/registry/apis/identity/legacy/testdata/postgres--query_team_members-team_1_members_page_1.sql
vendored
Executable file
@ -0,0 +1,21 @@
|
||||
SELECT
|
||||
tm.id as id,
|
||||
t.uid as team_uid,
|
||||
t.id as team_id,
|
||||
u.uid as user_uid,
|
||||
u.id as user_id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.login,
|
||||
tm.external,
|
||||
tm.created,
|
||||
tm.updated,
|
||||
tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
22
pkg/registry/apis/identity/legacy/testdata/postgres--query_team_members-team_1_members_page_2.sql
vendored
Executable file
22
pkg/registry/apis/identity/legacy/testdata/postgres--query_team_members-team_1_members_page_2.sql
vendored
Executable file
@ -0,0 +1,22 @@
|
||||
SELECT
|
||||
tm.id as id,
|
||||
t.uid as team_uid,
|
||||
t.id as team_id,
|
||||
u.uid as user_uid,
|
||||
u.id as user_id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.login,
|
||||
tm.external,
|
||||
tm.created,
|
||||
tm.updated,
|
||||
tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.id >= 2
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
@ -1,6 +1,6 @@
|
||||
SELECT id, uid, name, email, created, updated
|
||||
FROM "grafana"."team"
|
||||
WHERE org_id = 0
|
||||
AND id > 1
|
||||
AND id >= 2
|
||||
ORDER BY id asc
|
||||
LIMIT 2
|
||||
LIMIT 1
|
||||
|
@ -3,4 +3,4 @@ SELECT id, uid, name, email, created, updated
|
||||
WHERE org_id = 0
|
||||
AND uid = 'abc'
|
||||
ORDER BY id asc
|
||||
LIMIT 0
|
||||
LIMIT 1
|
||||
|
@ -3,6 +3,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND u.is_service_account = FALSE
|
||||
AND id > 1
|
||||
AND u.id >= 2
|
||||
ORDER BY u.id asc
|
||||
LIMIT 2
|
||||
LIMIT 1
|
||||
|
@ -3,6 +3,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND u.is_service_account = FALSE
|
||||
AND uid = 'abc'
|
||||
AND u.uid = 'abc'
|
||||
ORDER BY u.id asc
|
||||
LIMIT 0
|
||||
LIMIT 1
|
||||
|
@ -4,4 +4,5 @@ INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
@ -8,4 +8,5 @@ WHERE
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
@ -5,8 +5,9 @@ WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM "grafana"."team" t
|
||||
WHERE t.id >= 5
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
WHERE t.id >= 2
|
||||
ORDER BY t.id ASC LIMIT 1
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
|
21
pkg/registry/apis/identity/legacy/testdata/sqlite--query_team_members-team_1_members_page_1.sql
vendored
Executable file
21
pkg/registry/apis/identity/legacy/testdata/sqlite--query_team_members-team_1_members_page_1.sql
vendored
Executable file
@ -0,0 +1,21 @@
|
||||
SELECT
|
||||
tm.id as id,
|
||||
t.uid as team_uid,
|
||||
t.id as team_id,
|
||||
u.uid as user_uid,
|
||||
u.id as user_id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.login,
|
||||
tm.external,
|
||||
tm.created,
|
||||
tm.updated,
|
||||
tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
22
pkg/registry/apis/identity/legacy/testdata/sqlite--query_team_members-team_1_members_page_2.sql
vendored
Executable file
22
pkg/registry/apis/identity/legacy/testdata/sqlite--query_team_members-team_1_members_page_2.sql
vendored
Executable file
@ -0,0 +1,22 @@
|
||||
SELECT
|
||||
tm.id as id,
|
||||
t.uid as team_uid,
|
||||
t.id as team_id,
|
||||
u.uid as user_uid,
|
||||
u.id as user_id,
|
||||
u.name,
|
||||
u.email,
|
||||
u.login,
|
||||
tm.external,
|
||||
tm.created,
|
||||
tm.updated,
|
||||
tm.permission
|
||||
FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.id >= 2
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
@ -1,6 +1,6 @@
|
||||
SELECT id, uid, name, email, created, updated
|
||||
FROM "grafana"."team"
|
||||
WHERE org_id = 0
|
||||
AND id > 1
|
||||
AND id >= 2
|
||||
ORDER BY id asc
|
||||
LIMIT 2
|
||||
LIMIT 1
|
||||
|
@ -3,4 +3,4 @@ SELECT id, uid, name, email, created, updated
|
||||
WHERE org_id = 0
|
||||
AND uid = 'abc'
|
||||
ORDER BY id asc
|
||||
LIMIT 0
|
||||
LIMIT 1
|
||||
|
@ -3,6 +3,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND u.is_service_account = FALSE
|
||||
AND id > 1
|
||||
AND u.id >= 2
|
||||
ORDER BY u.id asc
|
||||
LIMIT 2
|
||||
LIMIT 1
|
||||
|
@ -3,6 +3,6 @@ SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND u.is_service_account = FALSE
|
||||
AND uid = 'abc'
|
||||
AND u.uid = 'abc'
|
||||
ORDER BY u.id asc
|
||||
LIMIT 0
|
||||
LIMIT 1
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
@ -13,15 +14,15 @@ import (
|
||||
type ListUserQuery struct {
|
||||
OrgID int64
|
||||
UID string
|
||||
ContinueID int64 // ContinueID
|
||||
Limit int64
|
||||
IsServiceAccount bool
|
||||
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListUserResult struct {
|
||||
Users []user.User
|
||||
ContinueID int64
|
||||
RV int64
|
||||
Users []user.User
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type GetUserDisplayQuery struct {
|
||||
@ -31,23 +32,28 @@ type GetUserDisplayQuery struct {
|
||||
}
|
||||
|
||||
type ListTeamQuery struct {
|
||||
OrgID int64
|
||||
UID string
|
||||
ContinueID int64 // ContinueID
|
||||
Limit int64
|
||||
OrgID int64
|
||||
UID string
|
||||
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamResult struct {
|
||||
Teams []team.Team
|
||||
ContinueID int64
|
||||
RV int64
|
||||
Teams []team.Team
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type TeamMember struct {
|
||||
ID int64
|
||||
TeamID int64
|
||||
TeamUID string
|
||||
UserID int64
|
||||
UserUID string
|
||||
Name string
|
||||
Email string
|
||||
Username string
|
||||
External bool
|
||||
Updated time.Time
|
||||
Created time.Time
|
||||
Permission team.PermissionType
|
||||
@ -66,23 +72,35 @@ type ListTeamBindingsQuery struct {
|
||||
// UID is team uid to list bindings for. If not set store should list bindings for all teams
|
||||
UID string
|
||||
OrgID int64
|
||||
ContinueID int64 // ContinueID
|
||||
Limit int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamBindingsResult struct {
|
||||
Bindings []TeamBinding
|
||||
ContinueID int64
|
||||
RV int64
|
||||
Bindings []TeamBinding
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
type ListTeamMembersQuery struct {
|
||||
UID string
|
||||
OrgID int64
|
||||
Pagination common.Pagination
|
||||
}
|
||||
|
||||
type ListTeamMembersResult struct {
|
||||
Continue int64
|
||||
Members []TeamMember
|
||||
}
|
||||
|
||||
// In every case, RBAC should be applied before calling, or before returning results to the requester
|
||||
type LegacyIdentityStore interface {
|
||||
ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error)
|
||||
GetDisplay(ctx context.Context, ns claims.NamespaceInfo, query GetUserDisplayQuery) (*ListUserResult, error)
|
||||
|
||||
ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error)
|
||||
|
||||
ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error)
|
||||
ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error)
|
||||
ListTeamMembers(ctx context.Context, ns claims.NamespaceInfo, query ListTeamMembersQuery) (*ListTeamMembersResult, error)
|
||||
|
||||
GetUserTeams(ctx context.Context, ns claims.NamespaceInfo, uid string) ([]team.Team, error)
|
||||
}
|
||||
|
@ -82,6 +82,7 @@ func (b *IdentityAPIBuilder) GetAPIGroupInfo(
|
||||
|
||||
teamResource := identityv0.TeamResourceInfo
|
||||
storage[teamResource.StoragePath()] = team.NewLegacyStore(b.Store)
|
||||
storage[teamResource.StoragePath("members")] = team.NewLegacyTeamMemberREST(b.Store)
|
||||
|
||||
teamBindingResource := identityv0.TeamBindingResourceInfo
|
||||
storage[teamBindingResource.StoragePath()] = team.NewLegacyBindingStore(b.Store)
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
identityv0 "github.com/grafana/grafana/pkg/apis/identity/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@ -64,14 +65,8 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
}
|
||||
query := legacy.ListUserQuery{
|
||||
OrgID: ns.OrgID,
|
||||
Limit: options.Limit,
|
||||
IsServiceAccount: true,
|
||||
}
|
||||
if options.Continue != "" {
|
||||
query.ContinueID, err = strconv.ParseInt(options.Continue, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid continue token")
|
||||
}
|
||||
Pagination: common.PaginationFromListOptions(options),
|
||||
}
|
||||
|
||||
found, err := s.store.ListUsers(ctx, ns, query)
|
||||
@ -83,12 +78,10 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
for _, item := range found.Users {
|
||||
list.Items = append(list.Items, *toSAItem(&item, ns.Value))
|
||||
}
|
||||
if found.ContinueID > 0 {
|
||||
list.ListMeta.Continue = strconv.FormatInt(found.ContinueID, 10)
|
||||
}
|
||||
if found.RV > 0 {
|
||||
list.ListMeta.ResourceVersion = strconv.FormatInt(found.RV, 10)
|
||||
}
|
||||
|
||||
list.ListMeta.Continue = common.OptionalFormatInt(found.Continue)
|
||||
list.ListMeta.ResourceVersion = common.OptionalFormatInt(found.RV)
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
@ -123,8 +116,8 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
|
||||
}
|
||||
query := legacy.ListUserQuery{
|
||||
OrgID: ns.OrgID,
|
||||
Limit: 1,
|
||||
IsServiceAccount: true,
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
}
|
||||
|
||||
found, err := s.store.ListUsers(ctx, ns, query)
|
||||
|
112
pkg/registry/apis/identity/team/rest_members.go
Normal file
112
pkg/registry/apis/identity/team/rest_members.go
Normal file
@ -0,0 +1,112 @@
|
||||
package team
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
identityv0 "github.com/grafana/grafana/pkg/apis/identity/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var (
|
||||
_ rest.Storage = (*LegacyTeamMemberREST)(nil)
|
||||
_ rest.Scoper = (*LegacyTeamMemberREST)(nil)
|
||||
_ rest.StorageMetadata = (*LegacyTeamMemberREST)(nil)
|
||||
_ rest.Connecter = (*LegacyTeamMemberREST)(nil)
|
||||
)
|
||||
|
||||
func NewLegacyTeamMemberREST(store legacy.LegacyIdentityStore) *LegacyTeamMemberREST {
|
||||
return &LegacyTeamMemberREST{store}
|
||||
}
|
||||
|
||||
type LegacyTeamMemberREST struct {
|
||||
store legacy.LegacyIdentityStore
|
||||
}
|
||||
|
||||
// New implements rest.Storage.
|
||||
func (s *LegacyTeamMemberREST) New() runtime.Object {
|
||||
return &identityv0.TeamMemberList{}
|
||||
}
|
||||
|
||||
// Destroy implements rest.Storage.
|
||||
func (s *LegacyTeamMemberREST) Destroy() {}
|
||||
|
||||
// NamespaceScoped implements rest.Scoper.
|
||||
func (s *LegacyTeamMemberREST) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ProducesMIMETypes implements rest.StorageMetadata.
|
||||
func (s *LegacyTeamMemberREST) ProducesMIMETypes(verb string) []string {
|
||||
return []string{"application/json"}
|
||||
}
|
||||
|
||||
// ProducesObject implements rest.StorageMetadata.
|
||||
func (s *LegacyTeamMemberREST) ProducesObject(verb string) interface{} {
|
||||
return s.New()
|
||||
}
|
||||
|
||||
// Connect implements rest.Connecter.
|
||||
func (s *LegacyTeamMemberREST) Connect(ctx context.Context, name string, options runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_ = common.PaginationFromListQuery(r.URL.Query())
|
||||
|
||||
res, err := s.store.ListTeamMembers(ctx, ns, legacy.ListTeamMembersQuery{
|
||||
UID: name,
|
||||
Pagination: common.PaginationFromListQuery(r.URL.Query()),
|
||||
})
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
list := &identityv0.TeamMemberList{Items: make([]identityv0.TeamMember, 0, len(res.Members))}
|
||||
|
||||
for _, m := range res.Members {
|
||||
list.Items = append(list.Items, mapToTeamMember(m))
|
||||
}
|
||||
|
||||
list.ListMeta.Continue = common.OptionalFormatInt(res.Continue)
|
||||
|
||||
responder.Object(http.StatusOK, list)
|
||||
}), nil
|
||||
}
|
||||
|
||||
// NewConnectOptions implements rest.Connecter.
|
||||
func (s *LegacyTeamMemberREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
return nil, false, ""
|
||||
}
|
||||
|
||||
// ConnectMethods implements rest.Connecter.
|
||||
func (s *LegacyTeamMemberREST) ConnectMethods() []string {
|
||||
return []string{http.MethodGet}
|
||||
}
|
||||
|
||||
var cfg = &setting.Cfg{}
|
||||
|
||||
func mapToTeamMember(m legacy.TeamMember) identityv0.TeamMember {
|
||||
return identityv0.TeamMember{
|
||||
IdentityDisplay: identityv0.IdentityDisplay{
|
||||
IdentityType: claims.TypeUser,
|
||||
UID: m.UserUID,
|
||||
Display: m.Name,
|
||||
AvatarURL: dtos.GetGravatarUrlWithDefault(cfg, m.Email, m.Name),
|
||||
InternalID: m.UserID,
|
||||
},
|
||||
External: m.External,
|
||||
Permission: mapPermisson(m.Permission),
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
identityv0 "github.com/grafana/grafana/pkg/apis/identity/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
@ -59,10 +60,6 @@ func (s *LegacyStore) ConvertToTable(ctx context.Context, object runtime.Object,
|
||||
}
|
||||
|
||||
func (s *LegacyStore) doList(ctx context.Context, ns claims.NamespaceInfo, query legacy.ListTeamQuery) (*identityv0.TeamList, error) {
|
||||
if query.Limit < 1 {
|
||||
query.Limit = 100
|
||||
}
|
||||
|
||||
rsp, err := s.store.ListTeams(ctx, ns, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -96,9 +93,10 @@ func (s *LegacyStore) doList(ctx context.Context, ns claims.NamespaceInfo, query
|
||||
})
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
if rsp.ContinueID > 0 {
|
||||
list.ListMeta.Continue = strconv.FormatInt(rsp.ContinueID, 10)
|
||||
}
|
||||
|
||||
list.ListMeta.Continue = common.OptionalFormatInt(rsp.Continue)
|
||||
list.ListMeta.ResourceVersion = common.OptionalFormatInt(rsp.RV)
|
||||
|
||||
return list, nil
|
||||
}
|
||||
|
||||
@ -107,17 +105,11 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := legacy.ListTeamQuery{
|
||||
OrgID: ns.OrgID,
|
||||
Limit: options.Limit,
|
||||
}
|
||||
if options.Continue != "" {
|
||||
query.ContinueID, err = strconv.ParseInt(options.Continue, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return s.doList(ctx, ns, query)
|
||||
|
||||
return s.doList(ctx, ns, legacy.ListTeamQuery{
|
||||
OrgID: ns.OrgID,
|
||||
Pagination: common.PaginationFromListOptions(options),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
@ -126,9 +118,9 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
|
||||
return nil, err
|
||||
}
|
||||
rsp, err := s.doList(ctx, ns, legacy.ListTeamQuery{
|
||||
OrgID: ns.OrgID,
|
||||
Limit: 1,
|
||||
UID: name,
|
||||
OrgID: ns.OrgID,
|
||||
UID: name,
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -72,8 +72,8 @@ func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *meta
|
||||
}
|
||||
|
||||
res, err := l.store.ListTeamBindings(ctx, ns, legacy.ListTeamBindingsQuery{
|
||||
UID: name,
|
||||
Limit: 1,
|
||||
UID: name,
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -95,14 +95,8 @@ func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
continueID, err := common.GetContinueID(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := l.store.ListTeamBindings(ctx, ns, legacy.ListTeamBindingsQuery{
|
||||
ContinueID: continueID,
|
||||
Limit: options.Limit,
|
||||
Pagination: common.PaginationFromListOptions(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -116,7 +110,7 @@ func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.
|
||||
list.Items = append(list.Items, mapToBindingObject(ns, b))
|
||||
}
|
||||
|
||||
list.ListMeta.Continue = common.OptionalFormatInt(res.ContinueID)
|
||||
list.ListMeta.Continue = common.OptionalFormatInt(res.Continue)
|
||||
list.ListMeta.ResourceVersion = common.OptionalFormatInt(res.RV)
|
||||
|
||||
return &list, nil
|
||||
|
@ -46,7 +46,7 @@ func (r *LegacyDisplayStore) NamespaceScoped() bool {
|
||||
|
||||
func (r *LegacyDisplayStore) GetSingularName() string {
|
||||
// not actually used anywhere, but required by SingularNameProvider
|
||||
return "IdentityDisplay"
|
||||
return "identitydisplay"
|
||||
}
|
||||
|
||||
func (r *LegacyDisplayStore) ProducesMIMETypes(verb string) []string {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
identityv0 "github.com/grafana/grafana/pkg/apis/identity/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@ -62,19 +63,12 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
query := legacy.ListUserQuery{
|
||||
OrgID: ns.OrgID,
|
||||
Limit: options.Limit,
|
||||
IsServiceAccount: false,
|
||||
}
|
||||
if options.Continue != "" {
|
||||
query.ContinueID, err = strconv.ParseInt(options.Continue, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid continue token")
|
||||
}
|
||||
}
|
||||
|
||||
found, err := s.store.ListUsers(ctx, ns, query)
|
||||
found, err := s.store.ListUsers(ctx, ns, legacy.ListUserQuery{
|
||||
OrgID: ns.OrgID,
|
||||
IsServiceAccount: false,
|
||||
Pagination: common.PaginationFromListOptions(options),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -83,12 +77,10 @@ func (s *LegacyStore) List(ctx context.Context, options *internalversion.ListOpt
|
||||
for _, item := range found.Users {
|
||||
list.Items = append(list.Items, *toUserItem(&item, ns.Value))
|
||||
}
|
||||
if found.ContinueID > 0 {
|
||||
list.ListMeta.Continue = strconv.FormatInt(found.ContinueID, 10)
|
||||
}
|
||||
if found.RV > 0 {
|
||||
list.ListMeta.ResourceVersion = strconv.FormatInt(found.RV, 10)
|
||||
}
|
||||
|
||||
list.ListMeta.Continue = common.OptionalFormatInt(found.Continue)
|
||||
list.ListMeta.ResourceVersion = common.OptionalFormatInt(found.RV)
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
@ -99,8 +91,8 @@ func (s *LegacyStore) Get(ctx context.Context, name string, options *metav1.GetO
|
||||
}
|
||||
query := legacy.ListUserQuery{
|
||||
OrgID: ns.OrgID,
|
||||
Limit: 1,
|
||||
IsServiceAccount: false,
|
||||
Pagination: common.Pagination{Limit: 1},
|
||||
}
|
||||
|
||||
found, err := s.store.ListUsers(ctx, ns, query)
|
||||
|
Loading…
Reference in New Issue
Block a user