MM-44101: GraphQL: Add sidebar categories at the top level (#20178)
Also, add a negate parameter to the team members to get all sidebarcategories except the default team. ```release-note NONE ```
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3c625743e5
Коммит
acae0031d1
@@ -53,7 +53,7 @@ func createEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
defer c.LogAuditRec(auditRec)
|
||||
|
||||
// Allow any user with CREATE_EMOJIS permission at Team level to create emojis at system level
|
||||
memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId, true)
|
||||
memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId, "", true)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
@@ -143,7 +143,7 @@ func deleteEmoji(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
auditRec.AddMeta("emoji", emoji)
|
||||
|
||||
// Allow any user with DELETE_EMOJIS permission at Team level to delete emojis at system level
|
||||
memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId, true)
|
||||
memberships, err := c.App.GetTeamMembersForUser(c.AppContext.Session().UserId, "", true)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
|
||||
@@ -132,8 +132,9 @@ func (r *resolver) License(ctx context.Context) (model.StringMap, error) {
|
||||
// match with api4.getTeamMembersForUser for teamID=""
|
||||
// and api4.getTeamMember for teamID != ""
|
||||
func (r *resolver) TeamMembers(ctx context.Context, args struct {
|
||||
UserID string
|
||||
TeamID string
|
||||
UserID string
|
||||
TeamID string
|
||||
ExcludeTeam bool
|
||||
}) ([]*teamMember, error) {
|
||||
c, err := getCtx(ctx)
|
||||
if err != nil {
|
||||
@@ -159,7 +160,7 @@ func (r *resolver) TeamMembers(ctx context.Context, args struct {
|
||||
return nil, c.Err
|
||||
}
|
||||
|
||||
if args.TeamID != "" {
|
||||
if args.TeamID != "" && !args.ExcludeTeam {
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), args.TeamID, model.PermissionViewTeam) {
|
||||
c.SetPermissionError(model.PermissionViewTeam)
|
||||
return nil, c.Err
|
||||
@@ -173,8 +174,13 @@ func (r *resolver) TeamMembers(ctx context.Context, args struct {
|
||||
return []*teamMember{{*tm}}, nil
|
||||
}
|
||||
|
||||
excludeTeamID := ""
|
||||
if args.TeamID != "" && args.ExcludeTeam {
|
||||
excludeTeamID = args.TeamID
|
||||
}
|
||||
|
||||
// Do not return archived team members
|
||||
members, appErr := c.App.GetTeamMembersForUser(args.UserID, false)
|
||||
members, appErr := c.App.GetTeamMembersForUser(args.UserID, excludeTeamID, false)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
@@ -301,6 +307,37 @@ func (*resolver) ChannelMembers(ctx context.Context, args struct {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// match with api4.getCategoriesForTeamForUser
|
||||
func (*resolver) SidebarCategories(ctx context.Context, args struct {
|
||||
UserID string
|
||||
TeamID string
|
||||
}) ([]*model.SidebarCategoryWithChannels, error) {
|
||||
c, err := getCtx(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Fallback to primary team logic
|
||||
if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), args.TeamID, model.PermissionViewTeam) {
|
||||
primaryTeam := *c.App.Config().TeamSettings.ExperimentalPrimaryTeam
|
||||
if primaryTeam != "" {
|
||||
team, appErr := c.App.GetTeamByName(primaryTeam)
|
||||
if appErr != nil {
|
||||
return []*model.SidebarCategoryWithChannels{}, appErr
|
||||
}
|
||||
args.TeamID = team.Id
|
||||
} else {
|
||||
return []*model.SidebarCategoryWithChannels{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
if args.UserID == model.Me {
|
||||
args.UserID = c.AppContext.Session().UserId
|
||||
}
|
||||
|
||||
return getSidebarCategories(c, args.UserID, args.TeamID)
|
||||
}
|
||||
|
||||
// getCtx extracts web.Context out of the usual request context.
|
||||
// Kind of an anti-pattern, but there are lots of methods attached to *web.Context
|
||||
// so we use it for now.
|
||||
|
||||
72
api4/resolver_sidebar_categories_test.go
Обычный файл
72
api4/resolver_sidebar_categories_test.go
Обычный файл
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package api4
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGraphQLSidebarCategories(t *testing.T) {
|
||||
os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
|
||||
defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
|
||||
th := Setup(t).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
var q struct {
|
||||
SidebarCategories []struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Sorting model.SidebarCategorySorting `json:"sorting"`
|
||||
ChannelIDs []string `json:"channelIds"`
|
||||
} `json:"sidebarCategories"`
|
||||
}
|
||||
|
||||
input := graphQLInput{
|
||||
OperationName: "sidebarCategories",
|
||||
Query: `
|
||||
query sidebarCategories($userId: String = "", $teamId: String = "") {
|
||||
sidebarCategories(userId: $userId, teamId: $teamId) {
|
||||
id
|
||||
displayName
|
||||
sorting
|
||||
channelIds
|
||||
}
|
||||
}
|
||||
`,
|
||||
Variables: map[string]interface{}{
|
||||
"userId": "me",
|
||||
"teamId": th.BasicTeam.Id,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := th.MakeGraphQLRequest(&input)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp.Errors, 0)
|
||||
require.NoError(t, json.Unmarshal(resp.Data, &q))
|
||||
assert.Len(t, q.SidebarCategories, 3)
|
||||
|
||||
categories, _, err := th.Client.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, "")
|
||||
require.NoError(t, err)
|
||||
|
||||
sort.Slice(q.SidebarCategories, func(i, j int) bool {
|
||||
return q.SidebarCategories[i].ID < q.SidebarCategories[j].ID
|
||||
})
|
||||
sort.Slice(categories.Categories, func(i, j int) bool {
|
||||
return categories.Categories[i].Id < categories.Categories[j].Id
|
||||
})
|
||||
|
||||
for i := range categories.Categories {
|
||||
assert.Equal(t, categories.Categories[i].Id, q.SidebarCategories[i].ID)
|
||||
assert.Equal(t, categories.Categories[i].DisplayName, q.SidebarCategories[i].DisplayName)
|
||||
assert.Equal(t, categories.Categories[i].Sorting, q.SidebarCategories[i].Sorting)
|
||||
assert.Equal(t, categories.Categories[i].ChannelIds(), q.SidebarCategories[i].ChannelIDs)
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/graph-gophers/dataloader/v6"
|
||||
"github.com/mattermost/mattermost-server/v6/model"
|
||||
"github.com/mattermost/mattermost-server/v6/web"
|
||||
)
|
||||
|
||||
// teamMember is an internal graphQL wrapper struct to add resolver methods.
|
||||
@@ -33,12 +34,16 @@ func (tm *teamMember) SidebarCategories(ctx context.Context) ([]*model.SidebarCa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), tm.UserId) {
|
||||
return getSidebarCategories(c, tm.UserId, tm.TeamId)
|
||||
}
|
||||
|
||||
func getSidebarCategories(c *web.Context, userID, teamID string) ([]*model.SidebarCategoryWithChannels, error) {
|
||||
if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), userID) {
|
||||
c.SetPermissionError(model.PermissionEditOtherUsers)
|
||||
return nil, c.Err
|
||||
}
|
||||
|
||||
categories, appErr := c.App.GetSidebarCategories(tm.UserId, tm.TeamId)
|
||||
categories, appErr := c.App.GetSidebarCategories(userID, teamID)
|
||||
if appErr != nil {
|
||||
return nil, appErr
|
||||
}
|
||||
|
||||
@@ -249,6 +249,48 @@ func TestGraphQLTeamMembers(t *testing.T) {
|
||||
expectedTeams[i].DisplayName = tm.Team.DisplayName
|
||||
}
|
||||
|
||||
// Negate team
|
||||
input = graphQLInput{
|
||||
OperationName: "teamMembers",
|
||||
Query: `
|
||||
query teamMembers($userId: String = "", $teamId: String = "") {
|
||||
teamMembers(userId: $userId, teamId: $teamId, excludeTeam: true) {
|
||||
team {
|
||||
id
|
||||
displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
Variables: map[string]interface{}{
|
||||
"userId": "me",
|
||||
"teamId": th.BasicTeam.Id,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err = th.MakeGraphQLRequest(&input)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp.Errors, 0)
|
||||
require.NoError(t, json.Unmarshal(resp.Data, &q))
|
||||
assert.Len(t, q.TeamMembers, 1)
|
||||
|
||||
input = graphQLInput{
|
||||
OperationName: "teamMembers",
|
||||
Query: `
|
||||
query teamMembers($userId: String = "", $teamId: String = "") {
|
||||
teamMembers(userId: $userId, teamId: $teamId) {
|
||||
team {
|
||||
id
|
||||
displayName
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
Variables: map[string]interface{}{
|
||||
"userId": "me",
|
||||
},
|
||||
}
|
||||
|
||||
// Removing from a team and ensuring we get the right response.
|
||||
th.UnlinkUserFromTeam(th.BasicUser, myTeam)
|
||||
resp, err = th.MakeGraphQLRequest(&input)
|
||||
|
||||
@@ -7,7 +7,8 @@ type Query {
|
||||
config(): StringMap!
|
||||
license(): StringMap!
|
||||
teamMembers(userId: String!,
|
||||
teamId: String = ""): [TeamMember]!
|
||||
teamId: String = "",
|
||||
excludeTeam: Boolean = false): [TeamMember]!
|
||||
channels(userId: String!,
|
||||
teamId: String = "",
|
||||
includeDeleted: Boolean = false,
|
||||
@@ -24,6 +25,8 @@ type Query {
|
||||
first: Int = 60,
|
||||
after: String = "",
|
||||
lastUpdateAt: Float = 0): [ChannelMember]!
|
||||
sidebarCategories(userId: String!,
|
||||
teamId: String!): [SidebarCategory]!
|
||||
}
|
||||
|
||||
scalar ChannelType
|
||||
|
||||
@@ -554,7 +554,7 @@ func getTeamMembersForUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
members, err := c.App.GetTeamMembersForUser(c.Params.UserId, true)
|
||||
members, err := c.App.GetTeamMembersForUser(c.Params.UserId, "", true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
|
||||
Ссылка в новой задаче
Block a user