mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
User: Support sort query param for user and org user, search endpoints (#75229)
* User: Add sort option to user search * Switch to an approach that uses the dashboard search options * Cable user sort on the org endpoint * Alias user table with u in org store * Add test and cover orgs/:orgID/users/search endpoint * Add test to userimpl store * Simplify the store_test with sortopts.ParseSortQueryParam * Account for PR feedback * Positive check * Update docs * Update docs * Switch to ErrOrFallback Co-authored-by: Karl Persson <kalle.persson@grafana.com> --------- Co-authored-by: Karl Persson <kalle.persson@grafana.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/models/roletype"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/search/model"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
)
|
||||
|
||||
@@ -178,11 +179,12 @@ type GetOrgUsersQuery struct {
|
||||
}
|
||||
|
||||
type SearchOrgUsersQuery struct {
|
||||
UserID int64 `xorm:"user_id"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
Query string
|
||||
Page int
|
||||
Limit int
|
||||
UserID int64 `xorm:"user_id"`
|
||||
OrgID int64 `xorm:"org_id"`
|
||||
Query string
|
||||
Page int
|
||||
Limit int
|
||||
SortOpts []model.SortOption
|
||||
// Flag used to allow oss edition to query users without access control
|
||||
DontEnforceAccessControl bool
|
||||
|
||||
|
||||
@@ -546,7 +546,7 @@ func (ss *sqlStore) SearchOrgUsers(ctx context.Context, query *org.SearchOrgUser
|
||||
}
|
||||
err := ss.db.WithDbSession(ctx, func(dbSession *db.Session) error {
|
||||
sess := dbSession.Table("org_user")
|
||||
sess.Join("INNER", ss.dialect.Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", ss.dialect.Quote("user")))
|
||||
sess.Join("INNER", []string{ss.dialect.Quote("user"), "u"}, "org_user.user_id=u.id")
|
||||
|
||||
whereConditions := make([]string, 0)
|
||||
whereParams := make([]any, 0)
|
||||
@@ -559,7 +559,7 @@ func (ss *sqlStore) SearchOrgUsers(ctx context.Context, query *org.SearchOrgUser
|
||||
whereParams = append(whereParams, query.UserID)
|
||||
}
|
||||
|
||||
whereConditions = append(whereConditions, fmt.Sprintf("%s.is_service_account = ?", ss.dialect.Quote("user")))
|
||||
whereConditions = append(whereConditions, "u.is_service_account = ?")
|
||||
whereParams = append(whereParams, ss.dialect.BooleanStr(false))
|
||||
|
||||
if query.User == nil {
|
||||
@@ -593,16 +593,25 @@ func (ss *sqlStore) SearchOrgUsers(ctx context.Context, query *org.SearchOrgUser
|
||||
sess.Cols(
|
||||
"org_user.org_id",
|
||||
"org_user.user_id",
|
||||
"user.email",
|
||||
"user.name",
|
||||
"user.login",
|
||||
"u.email",
|
||||
"u.name",
|
||||
"u.login",
|
||||
"org_user.role",
|
||||
"user.last_seen_at",
|
||||
"user.created",
|
||||
"user.updated",
|
||||
"user.is_disabled",
|
||||
"u.last_seen_at",
|
||||
"u.created",
|
||||
"u.updated",
|
||||
"u.is_disabled",
|
||||
)
|
||||
sess.Asc("user.email", "user.login")
|
||||
|
||||
if len(query.SortOpts) > 0 {
|
||||
for i := range query.SortOpts {
|
||||
for j := range query.SortOpts[i].Filter {
|
||||
sess.OrderBy(query.SortOpts[i].Filter[j].OrderBy())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sess.Asc("u.login", "u.email")
|
||||
}
|
||||
|
||||
if err := sess.Find(&result.OrgUsers); err != nil {
|
||||
return err
|
||||
@@ -611,7 +620,7 @@ func (ss *sqlStore) SearchOrgUsers(ctx context.Context, query *org.SearchOrgUser
|
||||
// get total count
|
||||
orgUser := org.OrgUser{}
|
||||
countSess := dbSession.Table("org_user").
|
||||
Join("INNER", ss.dialect.Quote("user"), fmt.Sprintf("org_user.user_id=%s.id", ss.dialect.Quote("user")))
|
||||
Join("INNER", []string{ss.dialect.Quote("user"), "u"}, "org_user.user_id=u.id")
|
||||
|
||||
if len(whereConditions) > 0 {
|
||||
countSess.Where(strings.Join(whereConditions, " AND "), whereParams...)
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
||||
"github.com/grafana/grafana/pkg/services/searchusers/sortopts"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
@@ -404,6 +405,42 @@ func TestIntegrationOrgUserDataAccess(t *testing.T) {
|
||||
require.Equal(t, len(result.OrgUsers), 1)
|
||||
require.Equal(t, result.OrgUsers[0].Email, ac1.Email)
|
||||
})
|
||||
t.Run("Can get organization users with custom ordering login-asc", func(t *testing.T) {
|
||||
sortOpts, err := sortopts.ParseSortQueryParam("login-asc,email-asc")
|
||||
require.NoError(t, err)
|
||||
query := org.SearchOrgUsersQuery{
|
||||
OrgID: ac1.OrgID,
|
||||
SortOpts: sortOpts,
|
||||
User: &user.SignedInUser{
|
||||
OrgID: ac1.OrgID,
|
||||
Permissions: map[int64]map[string][]string{ac1.OrgID: {accesscontrol.ActionOrgUsersRead: {accesscontrol.ScopeUsersAll}}},
|
||||
},
|
||||
}
|
||||
result, err := orgUserStore.SearchOrgUsers(context.Background(), &query)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(result.OrgUsers), 2)
|
||||
require.Equal(t, result.OrgUsers[0].Email, ac1.Email)
|
||||
require.Equal(t, result.OrgUsers[1].Email, ac2.Email)
|
||||
})
|
||||
t.Run("Can get organization users with custom ordering login-desc", func(t *testing.T) {
|
||||
sortOpts, err := sortopts.ParseSortQueryParam("login-desc,email-asc")
|
||||
require.NoError(t, err)
|
||||
query := org.SearchOrgUsersQuery{
|
||||
OrgID: ac1.OrgID,
|
||||
SortOpts: sortOpts,
|
||||
User: &user.SignedInUser{
|
||||
OrgID: ac1.OrgID,
|
||||
Permissions: map[int64]map[string][]string{ac1.OrgID: {accesscontrol.ActionOrgUsersRead: {accesscontrol.ScopeUsersAll}}},
|
||||
},
|
||||
}
|
||||
result, err := orgUserStore.SearchOrgUsers(context.Background(), &query)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(result.OrgUsers), 2)
|
||||
require.Equal(t, result.OrgUsers[0].Email, ac2.Email)
|
||||
require.Equal(t, result.OrgUsers[1].Email, ac1.Email)
|
||||
})
|
||||
t.Run("Cannot update role so no one is admin user", func(t *testing.T) {
|
||||
remCmd := org.RemoveOrgUserCommand{OrgID: ac1.OrgID, UserID: ac2.ID, ShouldDeleteOrphanedUser: true}
|
||||
err := orgUserStore.RemoveOrgUser(context.Background(), &remCmd)
|
||||
|
||||
Reference in New Issue
Block a user