diff --git a/api4/channel_category.go b/api4/channel_category.go index 6ff22b1f7f..f46b6a2fec 100644 --- a/api4/channel_category.go +++ b/api4/channel_category.go @@ -23,7 +23,7 @@ func getCategoriesForTeamForUser(c *Context, w http.ResponseWriter, r *http.Requ return } - categories, err := c.App.GetSidebarCategories(c.Params.UserId, c.Params.TeamId) + categories, err := c.App.GetSidebarCategoriesForTeamForUser(c.Params.UserId, c.Params.TeamId) if err != nil { c.Err = err return diff --git a/api4/resolver.go b/api4/resolver.go index d1ffcaea4e..ebb50590a7 100644 --- a/api4/resolver.go +++ b/api4/resolver.go @@ -309,8 +309,9 @@ func (*resolver) ChannelMembers(ctx context.Context, args struct { // match with api4.getCategoriesForTeamForUser func (*resolver) SidebarCategories(ctx context.Context, args struct { - UserID string - TeamID string + UserID string + TeamID string + ExcludeTeam bool }) ([]*model.SidebarCategoryWithChannels, error) { c, err := getCtx(ctx) if err != nil { @@ -335,7 +336,44 @@ func (*resolver) SidebarCategories(ctx context.Context, args struct { args.UserID = c.AppContext.Session().UserId } - return getSidebarCategories(c, args.UserID, args.TeamID) + if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), args.UserID) { + c.SetPermissionError(model.PermissionEditOtherUsers) + return nil, c.Err + } + + // If it's only for a single team. + var categories *model.OrderedSidebarCategories + var appErr *model.AppError + if !args.ExcludeTeam { + categories, appErr = c.App.GetSidebarCategoriesForTeamForUser(args.UserID, args.TeamID) + if appErr != nil { + return nil, appErr + } + } else { + opts := &store.SidebarCategorySearchOpts{ + TeamID: args.TeamID, + ExcludeTeam: args.ExcludeTeam, + } + categories, appErr = c.App.GetSidebarCategories(args.UserID, opts) + if appErr != nil { + return nil, appErr + } + } + + // TODO: look into optimizing this. + // create map + orderMap := make(map[string]*model.SidebarCategoryWithChannels, len(categories.Categories)) + for _, category := range categories.Categories { + orderMap[category.Id] = category + } + + // create a new slice based on the order + res := make([]*model.SidebarCategoryWithChannels, 0, len(categories.Categories)) + for _, categoryId := range categories.Order { + res = append(res, orderMap[categoryId]) + } + + return res, nil } // getCtx extracts web.Context out of the usual request context. diff --git a/api4/resolver_sidebar_categories_test.go b/api4/resolver_sidebar_categories_test.go index 75467dc4ec..b53cef4284 100644 --- a/api4/resolver_sidebar_categories_test.go +++ b/api4/resolver_sidebar_categories_test.go @@ -26,14 +26,15 @@ func TestGraphQLSidebarCategories(t *testing.T) { DisplayName string `json:"displayName"` Sorting model.SidebarCategorySorting `json:"sorting"` ChannelIDs []string `json:"channelIds"` + TeamID string `json:"teamId"` } `json:"sidebarCategories"` } input := graphQLInput{ OperationName: "sidebarCategories", Query: ` - query sidebarCategories($userId: String = "", $teamId: String = "") { - sidebarCategories(userId: $userId, teamId: $teamId) { + query sidebarCategories($userId: String = "", $teamId: String = "", $excludeTeam: Boolean = false) { + sidebarCategories(userId: $userId, teamId: $teamId, excludeTeam: $excludeTeam) { id displayName sorting @@ -69,4 +70,66 @@ func TestGraphQLSidebarCategories(t *testing.T) { assert.Equal(t, categories.Categories[i].Sorting, q.SidebarCategories[i].Sorting) assert.Equal(t, categories.Categories[i].ChannelIds(), q.SidebarCategories[i].ChannelIDs) } + + input = graphQLInput{ + OperationName: "sidebarCategories", + Query: ` + query sidebarCategories($userId: String = "", $teamId: String = "", $excludeTeam: Boolean = false) { + sidebarCategories(userId: $userId, teamId: $teamId, excludeTeam: $excludeTeam) { + id + displayName + sorting + channelIds + } + } + `, + Variables: map[string]interface{}{ + "userId": "me", + "teamId": th.BasicTeam.Id, + "excludeTeam": true, + }, + } + + 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, 0) + + // Adding a new team + myTeam := th.CreateTeam() + ch1 := th.CreateChannelWithClientAndTeam(th.Client, model.ChannelTypeOpen, myTeam.Id) + ch2 := th.CreateChannelWithClientAndTeam(th.Client, model.ChannelTypePrivate, myTeam.Id) + th.LinkUserToTeam(th.BasicUser, myTeam) + th.App.AddUserToChannel(th.BasicUser, ch1, false) + th.App.AddUserToChannel(th.BasicUser, ch2, false) + + input = graphQLInput{ + OperationName: "sidebarCategories", + Query: ` + query sidebarCategories($userId: String = "", $teamId: String = "", $excludeTeam: Boolean = false) { + sidebarCategories(userId: $userId, teamId: $teamId, excludeTeam: $excludeTeam) { + id + displayName + sorting + channelIds + teamId + } + } + `, + Variables: map[string]interface{}{ + "userId": "me", + "teamId": th.BasicTeam.Id, + "excludeTeam": true, + }, + } + + 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) + for _, cat := range q.SidebarCategories { + assert.Equal(t, myTeam.Id, cat.TeamID) + } } diff --git a/api4/resolver_team_member.go b/api4/resolver_team_member.go index 28591110cf..0145f5d6ba 100644 --- a/api4/resolver_team_member.go +++ b/api4/resolver_team_member.go @@ -9,7 +9,6 @@ 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. @@ -27,43 +26,6 @@ func (tm *teamMember) User(ctx context.Context) (*user, error) { return getGraphQLUser(ctx, tm.UserId) } -// match with api4.getCategoriesForTeamForUser -func (tm *teamMember) SidebarCategories(ctx context.Context) ([]*model.SidebarCategoryWithChannels, error) { - c, err := getCtx(ctx) - if err != nil { - return nil, err - } - - 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(userID, teamID) - if appErr != nil { - return nil, appErr - } - - // TODO: look into optimizing this. - // create map - orderMap := make(map[string]*model.SidebarCategoryWithChannels, len(categories.Categories)) - for _, category := range categories.Categories { - orderMap[category.Id] = category - } - - // create a new slice based on the order - res := make([]*model.SidebarCategoryWithChannels, 0, len(categories.Categories)) - for _, categoryId := range categories.Order { - res = append(res, orderMap[categoryId]) - } - - return res, nil -} - // match with api4.getRolesByNames func (tm *teamMember) Roles_(ctx context.Context) ([]*model.Role, error) { loader, err := getRolesLoader(ctx) diff --git a/api4/resolver_team_member_test.go b/api4/resolver_team_member_test.go index d89a677e95..701f29fba0 100644 --- a/api4/resolver_team_member_test.go +++ b/api4/resolver_team_member_test.go @@ -41,16 +41,10 @@ func TestGraphQLTeamMembers(t *testing.T) { SchemeManaged bool `json:"schemeManaged"` BuiltIn bool `json:"builtIn"` } `json:"roles"` - DeleteAt float64 `json:"deleteAt"` - SchemeGuest bool `json:"schemeGuest"` - SchemeUser bool `json:"schemeUser"` - SchemeAdmin bool `json:"schemeAdmin"` - SidebarCategories []struct { - ID string `json:"id"` - DisplayName string `json:"displayName"` - Sorting model.SidebarCategorySorting `json:"sorting"` - ChannelIDs []string `json:"channelIds"` - } `json:"sidebarCategories"` + DeleteAt float64 `json:"deleteAt"` + SchemeGuest bool `json:"schemeGuest"` + SchemeUser bool `json:"schemeUser"` + SchemeAdmin bool `json:"schemeAdmin"` } `json:"teamMembers"` } @@ -78,12 +72,6 @@ func TestGraphQLTeamMembers(t *testing.T) { schemeGuest schemeUser schemeAdmin - sidebarCategories { - id - displayName - sorting - channelIds - } } } `, @@ -114,23 +102,6 @@ func TestGraphQLTeamMembers(t *testing.T) { assert.False(t, tm.SchemeGuest) assert.True(t, tm.SchemeUser) assert.False(t, tm.SchemeAdmin) - - categories, _, err := th.Client.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id, "") - require.NoError(t, err) - - sort.Slice(tm.SidebarCategories, func(i, j int) bool { - return tm.SidebarCategories[i].ID < tm.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, tm.SidebarCategories[i].ID) - assert.Equal(t, categories.Categories[i].DisplayName, tm.SidebarCategories[i].DisplayName) - assert.Equal(t, categories.Categories[i].Sorting, tm.SidebarCategories[i].Sorting) - assert.Equal(t, categories.Categories[i].ChannelIds(), tm.SidebarCategories[i].ChannelIDs) - } }) t.Run("User+Team", func(t *testing.T) { diff --git a/api4/schema.graphqls b/api4/schema.graphqls index 3b6839285d..57eebb0b7a 100644 --- a/api4/schema.graphqls +++ b/api4/schema.graphqls @@ -26,7 +26,8 @@ type Query { after: String = "", lastUpdateAt: Float = 0): [ChannelMember]! sidebarCategories(userId: String!, - teamId: String!): [SidebarCategory]! + teamId: String!, + excludeTeam: Boolean = false): [SidebarCategory]! } scalar ChannelType @@ -158,7 +159,6 @@ type TeamMember { schemeGuest: Boolean! schemeUser: Boolean! schemeAdmin: Boolean! - sidebarCategories: [SidebarCategory]! } type SidebarCategory { @@ -168,6 +168,7 @@ type SidebarCategory { displayName: String! muted: Boolean! collapsed: Boolean! + teamId: String! channelIds: [String!]! } diff --git a/app/app_iface.go b/app/app_iface.go index 8d7c1e3b45..d202338c0d 100644 --- a/app/app_iface.go +++ b/app/app_iface.go @@ -743,7 +743,8 @@ type AppIface interface { GetSharedChannelRemotesStatus(channelID string) ([]*model.SharedChannelRemoteStatus, error) GetSharedChannels(page int, perPage int, opts model.SharedChannelFilterOpts) ([]*model.SharedChannel, *model.AppError) GetSharedChannelsCount(opts model.SharedChannelFilterOpts) (int64, error) - GetSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) + GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, *model.AppError) + GetSidebarCategoriesForTeamForUser(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) GetSidebarCategory(categoryId string) (*model.SidebarCategoryWithChannels, *model.AppError) GetSidebarCategoryOrder(userID, teamID string) ([]string, *model.AppError) GetSinglePost(postID string, includeDeleted bool) (*model.Post, *model.AppError) diff --git a/app/authorization_test.go b/app/authorization_test.go index 3c6f132869..c93aaa2529 100644 --- a/app/authorization_test.go +++ b/app/authorization_test.go @@ -113,14 +113,14 @@ func TestHasPermissionToCategory(t *testing.T) { session, err := th.App.CreateSession(&model.Session{UserId: th.BasicUser.Id, Props: model.StringMap{}}) require.Nil(t, err) - categories, err := th.App.GetSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id) + categories, err := th.App.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id) require.Nil(t, err) _, err = th.App.GetSession(session.Token) require.Nil(t, err) require.True(t, th.App.SessionHasPermissionToCategory(*session, th.BasicUser.Id, th.BasicTeam.Id, categories.Order[0])) - categories2, err := th.App.GetSidebarCategories(th.BasicUser2.Id, th.BasicTeam.Id) + categories2, err := th.App.GetSidebarCategoriesForTeamForUser(th.BasicUser2.Id, th.BasicTeam.Id) require.Nil(t, err) require.False(t, th.App.SessionHasPermissionToCategory(*session, th.BasicUser.Id, th.BasicTeam.Id, categories2.Order[0])) } diff --git a/app/channel_category.go b/app/channel_category.go index bc903e2851..1726218005 100644 --- a/app/channel_category.go +++ b/app/channel_category.go @@ -13,8 +13,8 @@ import ( "github.com/mattermost/mattermost-server/v6/store" ) -func (a *App) createInitialSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { - categories, nErr := a.Srv().Store.Channel().CreateInitialSidebarCategories(userID, teamID) +func (a *App) createInitialSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, *model.AppError) { + categories, nErr := a.Srv().Store.Channel().CreateInitialSidebarCategories(userID, opts) if nErr != nil { return nil, model.NewAppError("createInitialSidebarCategories", "app.channel.create_initial_sidebar_categories.internal_error", nil, nErr.Error(), http.StatusInternalServerError) } @@ -22,12 +22,39 @@ func (a *App) createInitialSidebarCategories(userID, teamID string) (*model.Orde return categories, nil } -func (a *App) GetSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { +func (a *App) GetSidebarCategoriesForTeamForUser(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { var appErr *model.AppError - categories, err := a.Srv().Store.Channel().GetSidebarCategories(userID, teamID) + categories, err := a.Srv().Store.Channel().GetSidebarCategoriesForTeamForUser(userID, teamID) if err == nil && len(categories.Categories) == 0 { // A user must always have categories, so migration must not have happened yet, and we should run it ourselves - categories, appErr = a.createInitialSidebarCategories(userID, teamID) + categories, appErr = a.createInitialSidebarCategories(userID, &store.SidebarCategorySearchOpts{ + TeamID: teamID, + ExcludeTeam: false, + }) + if appErr != nil { + return nil, appErr + } + } + + if err != nil { + var nfErr *store.ErrNotFound + switch { + case errors.As(err, &nfErr): + return nil, model.NewAppError("GetSidebarCategoriesForTeamForUser", "app.channel.sidebar_categories.app_error", nil, nfErr.Error(), http.StatusNotFound) + default: + return nil, model.NewAppError("GetSidebarCategoriesForTeamForUser", "app.channel.sidebar_categories.app_error", nil, err.Error(), http.StatusInternalServerError) + } + } + + return categories, nil +} + +func (a *App) GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, *model.AppError) { + var appErr *model.AppError + categories, err := a.Srv().Store.Channel().GetSidebarCategories(userID, opts) + if err == nil && len(categories.Categories) == 0 { + // A user must always have categories, so migration must not have happened yet, and we should run it ourselves + categories, appErr = a.createInitialSidebarCategories(userID, opts) if appErr != nil { return nil, appErr } diff --git a/app/channel_category_test.go b/app/channel_category_test.go index 6f586ddfd7..33e79a4783 100644 --- a/app/channel_category_test.go +++ b/app/channel_category_test.go @@ -94,7 +94,7 @@ func TestGetSidebarCategories(t *testing.T) { }) require.Nil(t, err) - categories, err := th.App.GetSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id) + categories, err := th.App.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id) assert.Nil(t, err) assert.Len(t, categories.Categories, 4) }) @@ -113,7 +113,7 @@ func TestGetSidebarCategories(t *testing.T) { }, 100) require.NoError(t, err) - categories, appErr := th.App.GetSidebarCategories(th.BasicUser.Id, team.Id) + categories, appErr := th.App.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, team.Id) assert.Nil(t, appErr) assert.Len(t, categories.Categories, 3) }) @@ -131,7 +131,7 @@ func TestGetSidebarCategories(t *testing.T) { require.NoError(t, err) }() - categories, appErr := th.App.GetSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id) + categories, appErr := th.App.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id) assert.Nil(t, categories) assert.NotNil(t, appErr) assert.Equal(t, "app.channel.sidebar_categories.app_error", appErr.Id) @@ -143,7 +143,7 @@ func TestUpdateSidebarCategories(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() - categories, err := th.App.GetSidebarCategories(th.BasicUser.Id, th.BasicTeam.Id) + categories, err := th.App.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, th.BasicTeam.Id) require.Nil(t, err) channelsCategory := categories.Categories[1] diff --git a/app/channel_test.go b/app/channel_test.go index 6f78e559ff..160a22fde3 100644 --- a/app/channel_test.go +++ b/app/channel_test.go @@ -219,7 +219,7 @@ func TestMoveChannel(t *testing.T) { assert.Equal(t, []string{}, updatedCategory.Channels) // And it should be on the new team instead - categories, err := th.App.GetSidebarCategories(th.BasicUser.Id, targetTeam.Id) + categories, err := th.App.GetSidebarCategoriesForTeamForUser(th.BasicUser.Id, targetTeam.Id) require.Nil(t, err) require.Equal(t, model.SidebarCategoryChannels, categories.Categories[1].Type) assert.Contains(t, categories.Categories[1].Channels, channel.Id) diff --git a/app/opentracing/opentracing_layer.go b/app/opentracing/opentracing_layer.go index 2bcdf12848..3c4410122b 100644 --- a/app/opentracing/opentracing_layer.go +++ b/app/opentracing/opentracing_layer.go @@ -8951,7 +8951,7 @@ func (a *OpenTracingAppLayer) GetSharedChannelsCount(opts model.SharedChannelFil return resultVar0, resultVar1 } -func (a *OpenTracingAppLayer) GetSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { +func (a *OpenTracingAppLayer) GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, *model.AppError) { origCtx := a.ctx span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSidebarCategories") @@ -8963,7 +8963,29 @@ func (a *OpenTracingAppLayer) GetSidebarCategories(userID string, teamID string) }() defer span.Finish() - resultVar0, resultVar1 := a.app.GetSidebarCategories(userID, teamID) + resultVar0, resultVar1 := a.app.GetSidebarCategories(userID, opts) + + if resultVar1 != nil { + span.LogFields(spanlog.Error(resultVar1)) + ext.Error.Set(span, true) + } + + return resultVar0, resultVar1 +} + +func (a *OpenTracingAppLayer) GetSidebarCategoriesForTeamForUser(userID string, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { + origCtx := a.ctx + span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetSidebarCategoriesForTeamForUser") + + a.ctx = newCtx + a.app.Srv().Store.SetContext(newCtx) + defer func() { + a.app.Srv().Store.SetContext(origCtx) + a.ctx = origCtx + }() + + defer span.Finish() + resultVar0, resultVar1 := a.app.GetSidebarCategoriesForTeamForUser(userID, teamID) if resultVar1 != nil { span.LogFields(spanlog.Error(resultVar1)) diff --git a/app/plugin_api.go b/app/plugin_api.go index 3c7637a880..fb17cc2059 100644 --- a/app/plugin_api.go +++ b/app/plugin_api.go @@ -493,7 +493,7 @@ func (api *PluginAPI) CreateChannelSidebarCategory(userID, teamID string, newCat } func (api *PluginAPI) GetChannelSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, *model.AppError) { - return api.app.GetSidebarCategories(userID, teamID) + return api.app.GetSidebarCategoriesForTeamForUser(userID, teamID) } func (api *PluginAPI) UpdateChannelSidebarCategories(userID, teamID string, categories []*model.SidebarCategoryWithChannels) ([]*model.SidebarCategoryWithChannels, *model.AppError) { diff --git a/app/team.go b/app/team.go index 6fd8c35536..5b2d5279f9 100644 --- a/app/team.go +++ b/app/team.go @@ -804,7 +804,11 @@ func (a *App) JoinUserToTeam(c *request.Context, team *model.Team, user *model.U return nil, model.NewAppError("JoinUserToTeam", "app.user.update_update.app_error", nil, err.Error(), http.StatusInternalServerError) } - if _, err := a.createInitialSidebarCategories(user.Id, team.Id); err != nil { + opts := &store.SidebarCategorySearchOpts{ + TeamID: team.Id, + ExcludeTeam: false, + } + if _, err := a.createInitialSidebarCategories(user.Id, opts); err != nil { mlog.Warn( "Encountered an issue creating default sidebar categories.", mlog.String("user_id", user.Id), diff --git a/app/team_test.go b/app/team_test.go index a41e8e9ef2..ce84ff5877 100644 --- a/app/team_test.go +++ b/app/team_test.go @@ -185,7 +185,7 @@ func TestAddUserToTeam(t *testing.T) { _, _, err := th.App.AddUserToTeam(th.Context, team.Id, user.Id, "") require.Nil(t, err) - res, err := th.App.GetSidebarCategories(user.Id, team.Id) + res, err := th.App.GetSidebarCategoriesForTeamForUser(user.Id, team.Id) require.Nil(t, err) assert.Len(t, res.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type) @@ -429,7 +429,7 @@ func TestAddUserToTeamByToken(t *testing.T) { _, _, err := th.App.AddUserToTeamByToken(th.Context, user.Id, token.Token) require.Nil(t, err) - res, err := th.App.GetSidebarCategories(user.Id, team.Id) + res, err := th.App.GetSidebarCategoriesForTeamForUser(user.Id, team.Id) require.Nil(t, err) assert.Len(t, res.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type) diff --git a/store/layer_generators/main.go b/store/layer_generators/main.go index f630dca1d9..3f9089246e 100644 --- a/store/layer_generators/main.go +++ b/store/layer_generators/main.go @@ -284,7 +284,7 @@ func generateLayer(name, templateFile string) ([]byte, error) { switch param.Type { case "ChannelSearchOpts", "UserGetByIdsOpts", "ThreadMembershipOpts": paramsWithType = append(paramsWithType, fmt.Sprintf("%s store.%s", param.Name, param.Type)) - case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts": + case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts", "*SidebarCategorySearchOpts": paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.%s", param.Name, strings.TrimPrefix(param.Type, "*"))) default: paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type)) @@ -298,7 +298,7 @@ func generateLayer(name, templateFile string) ([]byte, error) { switch param.Type { case "ChannelSearchOpts", "UserGetByIdsOpts", "ThreadMembershipOpts": paramsWithType = append(paramsWithType, fmt.Sprintf("%s store.%s", param.Name, param.Type)) - case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts": + case "*UserGetByIdsOpts", "*ChannelMemberGraphQLSearchOpts", "*SidebarCategorySearchOpts": paramsWithType = append(paramsWithType, fmt.Sprintf("%s *store.%s", param.Name, strings.TrimPrefix(param.Type, "*"))) default: paramsWithType = append(paramsWithType, fmt.Sprintf("%s %s", param.Name, param.Type)) diff --git a/store/opentracinglayer/opentracinglayer.go b/store/opentracinglayer/opentracinglayer.go index 93fd26ad2a..9215f35763 100644 --- a/store/opentracinglayer/opentracinglayer.go +++ b/store/opentracinglayer/opentracinglayer.go @@ -710,7 +710,7 @@ func (s *OpenTracingLayerChannelStore) CreateDirectChannel(userID *model.User, o return result, err } -func (s *OpenTracingLayerChannelStore) CreateInitialSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { +func (s *OpenTracingLayerChannelStore) CreateInitialSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.CreateInitialSidebarCategories") s.Root.Store.SetContext(newCtx) @@ -719,7 +719,7 @@ func (s *OpenTracingLayerChannelStore) CreateInitialSidebarCategories(userID str }() defer span.Finish() - result, err := s.ChannelStore.CreateInitialSidebarCategories(userID, teamID) + result, err := s.ChannelStore.CreateInitialSidebarCategories(userID, opts) if err != nil { span.LogFields(spanlog.Error(err)) ext.Error.Set(span, true) @@ -1641,7 +1641,7 @@ func (s *OpenTracingLayerChannelStore) GetPublicChannelsForTeam(teamID string, o return result, err } -func (s *OpenTracingLayerChannelStore) GetSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { +func (s *OpenTracingLayerChannelStore) GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { origCtx := s.Root.Store.Context() span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetSidebarCategories") s.Root.Store.SetContext(newCtx) @@ -1650,7 +1650,25 @@ func (s *OpenTracingLayerChannelStore) GetSidebarCategories(userID string, teamI }() defer span.Finish() - result, err := s.ChannelStore.GetSidebarCategories(userID, teamID) + result, err := s.ChannelStore.GetSidebarCategories(userID, opts) + if err != nil { + span.LogFields(spanlog.Error(err)) + ext.Error.Set(span, true) + } + + return result, err +} + +func (s *OpenTracingLayerChannelStore) GetSidebarCategoriesForTeamForUser(userID string, teamID string) (*model.OrderedSidebarCategories, error) { + origCtx := s.Root.Store.Context() + span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetSidebarCategoriesForTeamForUser") + s.Root.Store.SetContext(newCtx) + defer func() { + s.Root.Store.SetContext(origCtx) + }() + + defer span.Finish() + result, err := s.ChannelStore.GetSidebarCategoriesForTeamForUser(userID, teamID) if err != nil { span.LogFields(spanlog.Error(err)) ext.Error.Set(span, true) diff --git a/store/retrylayer/retrylayer.go b/store/retrylayer/retrylayer.go index 0f09d785f9..aada214d48 100644 --- a/store/retrylayer/retrylayer.go +++ b/store/retrylayer/retrylayer.go @@ -773,11 +773,11 @@ func (s *RetryLayerChannelStore) CreateDirectChannel(userID *model.User, otherUs } -func (s *RetryLayerChannelStore) CreateInitialSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { +func (s *RetryLayerChannelStore) CreateInitialSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { tries := 0 for { - result, err := s.ChannelStore.CreateInitialSidebarCategories(userID, teamID) + result, err := s.ChannelStore.CreateInitialSidebarCategories(userID, opts) if err == nil { return result, nil } @@ -1850,11 +1850,32 @@ func (s *RetryLayerChannelStore) GetPublicChannelsForTeam(teamID string, offset } -func (s *RetryLayerChannelStore) GetSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { +func (s *RetryLayerChannelStore) GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { tries := 0 for { - result, err := s.ChannelStore.GetSidebarCategories(userID, teamID) + result, err := s.ChannelStore.GetSidebarCategories(userID, opts) + if err == nil { + return result, nil + } + if !isRepeatableError(err) { + return result, err + } + tries++ + if tries >= 3 { + err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures") + return result, err + } + timepkg.Sleep(100 * timepkg.Millisecond) + } + +} + +func (s *RetryLayerChannelStore) GetSidebarCategoriesForTeamForUser(userID string, teamID string) (*model.OrderedSidebarCategories, error) { + + tries := 0 + for { + result, err := s.ChannelStore.GetSidebarCategoriesForTeamForUser(userID, teamID) if err == nil { return result, nil } diff --git a/store/sqlstore/channel_store_categories.go b/store/sqlstore/channel_store_categories.go index a203fa7090..4b3479bbfc 100644 --- a/store/sqlstore/channel_store_categories.go +++ b/store/sqlstore/channel_store_categories.go @@ -4,6 +4,7 @@ package sqlstore import ( + "context" "fmt" sq "github.com/mattermost/squirrel" @@ -19,18 +20,27 @@ type dbSelecter interface { Select(i interface{}, query string, args ...interface{}) error } -func (s SqlChannelStore) CreateInitialSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, error) { +func (s SqlChannelStore) CreateInitialSidebarCategories(userId string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { transaction, err := s.GetMasterX().Beginx() if err != nil { return nil, errors.Wrap(err, "CreateInitialSidebarCategories: begin_transaction") } defer finalizeTransactionX(transaction) - if err = s.createInitialSidebarCategoriesT(transaction, userId, teamId); err != nil { + teamsWithExclude, err := s.SqlStore.stores.team.GetTeamsForUser(context.Background(), userId, opts.TeamID, false) + if err != nil { + return nil, errors.Wrap(err, "CreateInitialSidebarCategories: GetTeamsForUser") + } + excludedTeamIDs := make([]string, 0, len(teamsWithExclude)) + for _, tm := range teamsWithExclude { + excludedTeamIDs = append(excludedTeamIDs, tm.TeamId) + } + + if err = s.createInitialSidebarCategoriesT(transaction, userId, excludedTeamIDs, opts); err != nil { return nil, errors.Wrap(err, "CreateInitialSidebarCategories: createInitialSidebarCategoriesT") } - oc, err := s.getSidebarCategoriesT(transaction, userId, teamId) + oc, err := s.getSidebarCategoriesT(transaction, userId, opts) if err != nil { return nil, errors.Wrap(err, "CreateInitialSidebarCategories: getSidebarCategoriesT") } @@ -42,82 +52,119 @@ func (s SqlChannelStore) CreateInitialSidebarCategories(userId, teamId string) ( return oc, nil } -func (s SqlChannelStore) createInitialSidebarCategoriesT(transaction *sqlxTxWrapper, userId, teamId string) error { - selectQuery, selectParams, _ := s.getQueryBuilder(). - Select("Type"). +func (s SqlChannelStore) createInitialSidebarCategoriesT(transaction *sqlxTxWrapper, userId string, excludedTeamIDs []string, opts *store.SidebarCategorySearchOpts) error { + query := s.getQueryBuilder(). + Select("Type, TeamId"). From("SidebarCategories"). Where(sq.Eq{ "UserId": userId, - "TeamId": teamId, - "Type": []model.SidebarCategoryType{model.SidebarCategoryFavorites, model.SidebarCategoryChannels, model.SidebarCategoryDirectMessages}, - }).ToSql() + "Type": []model.SidebarCategoryType{ + model.SidebarCategoryFavorites, + model.SidebarCategoryChannels, + model.SidebarCategoryDirectMessages, + }, + }) - existingTypes := []model.SidebarCategoryType{} - err := transaction.Select(&existingTypes, selectQuery, selectParams...) + if !opts.ExcludeTeam { + query = query.Where(sq.Eq{"TeamId": opts.TeamID}) + } else { + query = query.Where(sq.NotEq{"TeamId": opts.TeamID}) + } + + selectQuery, selectParams, err := query.ToSql() + if err != nil { + return errors.Wrap(err, "createInitialSidebarCategoriesT_Tosql") + } + + existingTypes := []struct { + Type model.SidebarCategoryType + TeamId string + }{} + err = transaction.Select(&existingTypes, selectQuery, selectParams...) if err != nil { return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to select existing categories") } - hasCategoryOfType := make(map[model.SidebarCategoryType]bool, len(existingTypes)) + hasCategoryOfType := make(map[model.SidebarCategoryType]map[string]bool, len(existingTypes)) for _, existingType := range existingTypes { - hasCategoryOfType[existingType] = true + if hasCategoryOfType[existingType.Type] == nil { + hasCategoryOfType[existingType.Type] = make(map[string]bool) + hasCategoryOfType[existingType.Type][existingType.TeamId] = true + } } - // Use deterministic IDs for default categories to prevent potentially creating multiple copies of a default category - favoritesCategoryId := fmt.Sprintf("%s_%s_%s", model.SidebarCategoryFavorites, userId, teamId) - channelsCategoryId := fmt.Sprintf("%s_%s_%s", model.SidebarCategoryChannels, userId, teamId) - directMessagesCategoryId := fmt.Sprintf("%s_%s_%s", model.SidebarCategoryDirectMessages, userId, teamId) + insertBuilder := s.getQueryBuilder().Insert("SidebarCategories"). + Columns("Id, UserId, TeamId, SortOrder, Sorting, Type, DisplayName, Muted, Collapsed") - if !hasCategoryOfType[model.SidebarCategoryFavorites] { + hasInsert := false + + getRequiredTeamIDs := func(category model.SidebarCategoryType, opts *store.SidebarCategorySearchOpts) []string { + // if category == nil - nothing + // if not exclude - just that team + // otherwise get all teams excluding that team + // if != nil - then partial + // if not exclude, and team exists in map then skip. + // otherwise, get all teams excluding that team, subtract all items from map. + if hasCategoryOfType[category] == nil { + // If not exclude, do for only single team + // if exclude, get all teams, excluding that team + if !opts.ExcludeTeam { + return []string{opts.TeamID} + } + return excludedTeamIDs + } + mapEntry := hasCategoryOfType[category] + if !opts.ExcludeTeam && mapEntry[opts.TeamID] { + // continue, nothing to do since entry already exists. + } else { + for i, tID := range excludedTeamIDs { + if mapEntry[tID] { + // remove from slice + copy(excludedTeamIDs[i:], excludedTeamIDs[i+1:]) + excludedTeamIDs[len(excludedTeamIDs)-1] = "" + excludedTeamIDs = excludedTeamIDs[:len(excludedTeamIDs)-1] + } + } + return excludedTeamIDs + } + return []string{} + } + + teamIDs := getRequiredTeamIDs(model.SidebarCategoryFavorites, opts) + for _, teamID := range teamIDs { + // Use deterministic IDs for default categories to prevent potentially creating multiple copies of a default category + favoritesCategoryId := fmt.Sprintf("%s_%s_%s", model.SidebarCategoryFavorites, userId, teamID) // Create the SidebarChannels first since there's more opportunity for something to fail here - if err := s.migrateFavoritesToSidebarT(transaction, userId, teamId, favoritesCategoryId); err != nil { + if err := s.migrateFavoritesToSidebarT(transaction, userId, teamID, favoritesCategoryId); err != nil { return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to migrate favorites to sidebar") } - if _, err := transaction.NamedExec(`INSERT INTO - SidebarCategories(Id, UserId, TeamId, SortOrder, Sorting, Type, DisplayName, Muted, Collapsed) - VALUES(:Id, :UserId, :TeamId, :SortOrder, :Sorting, :Type, :DisplayName, :Muted, :Collapsed)`, &model.SidebarCategory{ - DisplayName: "Favorites", // This will be retranslated by the client into the user's locale - Id: favoritesCategoryId, - UserId: userId, - TeamId: teamId, - Sorting: model.SidebarCategorySortDefault, - SortOrder: model.DefaultSidebarSortOrderFavorites, - Type: model.SidebarCategoryFavorites, - }); err != nil { - return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert favorites category") - } + insertBuilder = insertBuilder.Values(favoritesCategoryId, userId, teamID, model.DefaultSidebarSortOrderFavorites, model.SidebarCategorySortDefault, model.SidebarCategoryFavorites, "Favorites" /* This will be retranslated by the client into the user's locale */, false, false) + hasInsert = true } - if !hasCategoryOfType[model.SidebarCategoryChannels] { - if _, err := transaction.NamedExec(`INSERT INTO - SidebarCategories(Id, UserId, TeamId, SortOrder, Sorting, Type, DisplayName, Muted, Collapsed) - VALUES(:Id, :UserId, :TeamId, :SortOrder, :Sorting, :Type, :DisplayName, :Muted, :Collapsed)`, &model.SidebarCategory{ - DisplayName: "Channels", // This will be retranslated by the client into the user's locale - Id: channelsCategoryId, - UserId: userId, - TeamId: teamId, - Sorting: model.SidebarCategorySortDefault, - SortOrder: model.DefaultSidebarSortOrderChannels, - Type: model.SidebarCategoryChannels, - }); err != nil { - return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert channels category") - } + teamIDs = getRequiredTeamIDs(model.SidebarCategoryChannels, opts) + for _, teamID := range teamIDs { + channelsCategoryId := fmt.Sprintf("%s_%s_%s", model.SidebarCategoryChannels, userId, teamID) + insertBuilder = insertBuilder.Values(channelsCategoryId, userId, teamID, model.DefaultSidebarSortOrderChannels, model.SidebarCategorySortDefault, model.SidebarCategoryChannels, "Channels" /* This will be retranslated by the client into the user's locale */, false, false) + hasInsert = true } - if !hasCategoryOfType[model.SidebarCategoryDirectMessages] { - if _, err := transaction.NamedExec(`INSERT INTO - SidebarCategories(Id, UserId, TeamId, SortOrder, Sorting, Type, DisplayName, Muted, Collapsed) - VALUES(:Id, :UserId, :TeamId, :SortOrder, :Sorting, :Type, :DisplayName, :Muted, :Collapsed)`, &model.SidebarCategory{ - DisplayName: "Direct Messages", // This will be retranslated by the client into the user's locale - Id: directMessagesCategoryId, - UserId: userId, - TeamId: teamId, - Sorting: model.SidebarCategorySortRecent, - SortOrder: model.DefaultSidebarSortOrderDMs, - Type: model.SidebarCategoryDirectMessages, - }); err != nil { - return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert direct messages category") + teamIDs = getRequiredTeamIDs(model.SidebarCategoryDirectMessages, opts) + for _, teamID := range teamIDs { + directMessagesCategoryId := fmt.Sprintf("%s_%s_%s", model.SidebarCategoryDirectMessages, userId, teamID) + insertBuilder = insertBuilder.Values(directMessagesCategoryId, userId, teamID, model.DefaultSidebarSortOrderDMs, model.SidebarCategorySortRecent, model.SidebarCategoryDirectMessages, "Direct Messages" /* This will be retranslated by the client into the user's locale */, false, false) + hasInsert = true + } + + if hasInsert { + sql, args, err := insertBuilder.ToSql() + if err != nil { + return errors.Wrap(err, "insertSidebarCategories_Tosql") + } + _, err = transaction.Exec(sql, args...) + if err != nil { + return errors.Wrap(err, "createInitialSidebarCategoriesT: failed to insert categories") } } @@ -252,7 +299,11 @@ func (s SqlChannelStore) CreateSidebarCategory(userId, teamId string, newCategor defer finalizeTransactionX(transaction) - categoriesWithOrder, err := s.getSidebarCategoriesT(transaction, userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + categoriesWithOrder, err := s.getSidebarCategoriesT(transaction, userId, opts) if err != nil { return nil, err } else if len(categoriesWithOrder.Categories) == 0 { @@ -468,28 +519,35 @@ func (s SqlChannelStore) GetSidebarCategory(categoryId string) (*model.SidebarCa return s.completePopulatingCategoryChannels(result) } -func (s SqlChannelStore) getSidebarCategoriesT(db dbSelecter, userId, teamId string) (*model.OrderedSidebarCategories, error) { +func (s SqlChannelStore) getSidebarCategoriesT(db dbSelecter, userId string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { oc := model.OrderedSidebarCategories{ Categories: make(model.SidebarCategoriesWithChannels, 0), Order: make([]string, 0), } categories := []*sidebarCategoryForJoin{} - query, args, err := s.getQueryBuilder(). + query := s.getQueryBuilder(). Select("SidebarCategories.*", "SidebarChannels.ChannelId"). From("SidebarCategories"). LeftJoin("SidebarChannels ON SidebarChannels.CategoryId=Id"). Where(sq.And{ sq.Eq{"SidebarCategories.UserId": userId}, - sq.Eq{"SidebarCategories.TeamId": teamId}, }). - OrderBy("SidebarCategories.SortOrder ASC, SidebarChannels.SortOrder ASC").ToSql() + OrderBy("SidebarCategories.SortOrder ASC, SidebarChannels.SortOrder ASC") + + if opts.ExcludeTeam { + query = query.Where(sq.NotEq{"SidebarCategories.TeamId": opts.TeamID}) + } else { + query = query.Where(sq.Eq{"SidebarCategories.TeamId": opts.TeamID}) + } + + sql, args, err := query.ToSql() if err != nil { return nil, errors.Wrap(err, "sidebar_categories_tosql") } - if err := db.Select(&categories, query, args...); err != nil { - return nil, store.NewErrNotFound("SidebarCategories", fmt.Sprintf("userId=%s,teamId=%s", userId, teamId)) + if err := db.Select(&categories, sql, args...); err != nil { + return nil, store.NewErrNotFound("SidebarCategories", fmt.Sprintf("userId=%s,teamId=%s", userId, opts.TeamID)) } for _, category := range categories { @@ -521,8 +579,16 @@ func (s SqlChannelStore) getSidebarCategoriesT(db dbSelecter, userId, teamId str return &oc, nil } -func (s SqlChannelStore) GetSidebarCategories(userId, teamId string) (*model.OrderedSidebarCategories, error) { - return s.getSidebarCategoriesT(s.GetReplicaX(), userId, teamId) +func (s SqlChannelStore) GetSidebarCategoriesForTeamForUser(userId, teamId string) (*model.OrderedSidebarCategories, error) { + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + return s.getSidebarCategoriesT(s.GetReplicaX(), userId, opts) +} + +func (s SqlChannelStore) GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { + return s.getSidebarCategoriesT(s.GetReplicaX(), userID, opts) } func (s SqlChannelStore) GetSidebarCategoryOrder(userId, teamId string) ([]string, error) { diff --git a/store/store.go b/store/store.go index 14490aa9c0..64198e7635 100644 --- a/store/store.go +++ b/store/store.go @@ -262,8 +262,9 @@ type ChannelStore interface { MigrateChannelMembers(fromChannelID string, fromUserID string) (map[string]string, error) ResetAllChannelSchemes() error ClearAllCustomRoleAssignments() error - CreateInitialSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, error) - GetSidebarCategories(userID, teamID string) (*model.OrderedSidebarCategories, error) + CreateInitialSidebarCategories(userID string, opts *SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) + GetSidebarCategoriesForTeamForUser(userID, teamID string) (*model.OrderedSidebarCategories, error) + GetSidebarCategories(userID string, opts *SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) GetSidebarCategory(categoryID string) (*model.SidebarCategoryWithChannels, error) GetSidebarCategoryOrder(userID, teamID string) ([]string, error) CreateSidebarCategory(userID, teamID string, newCategory *model.SidebarCategoryWithChannels) (*model.SidebarCategoryWithChannels, error) @@ -1013,3 +1014,10 @@ type ChannelMemberGraphQLSearchOpts struct { LastUpdateAt int ExcludeTeam bool } + +// SidebarCategorySearchOpts contains the options for a graphQL query +// to get the sidebar categories. +type SidebarCategorySearchOpts struct { + TeamID string + ExcludeTeam bool +} diff --git a/store/storetest/channel_store_categories.go b/store/storetest/channel_store_categories.go index a6c3061387..faaa0dd75c 100644 --- a/store/storetest/channel_store_categories.go +++ b/store/storetest/channel_store_categories.go @@ -33,14 +33,19 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) assert.NoError(t, nErr) - assert.Len(t, res.Categories, 3) + require.Len(t, res.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type) assert.Equal(t, model.SidebarCategoryChannels, res.Categories[1].Type) assert.Equal(t, model.SidebarCategoryDirectMessages, res.Categories[2].Type) - res2, err := ss.Channel().GetSidebarCategories(userId, teamId) + res2, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) assert.NoError(t, err) assert.Equal(t, res, res2) }) @@ -49,20 +54,24 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) userId2 := model.NewId() - res, nErr = ss.Channel().CreateInitialSidebarCategories(userId2, teamId) + res, nErr = ss.Channel().CreateInitialSidebarCategories(userId2, opts) assert.NoError(t, nErr) assert.Len(t, res.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type) assert.Equal(t, model.SidebarCategoryChannels, res.Categories[1].Type) assert.Equal(t, model.SidebarCategoryDirectMessages, res.Categories[2].Type) - res2, err := ss.Channel().GetSidebarCategories(userId2, teamId) + res2, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId2, teamId) assert.NoError(t, err) assert.Equal(t, res, res2) }) @@ -71,20 +80,27 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) teamId2 := model.NewId() - - res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, teamId2) + opts = &store.SidebarCategorySearchOpts{ + TeamID: teamId2, + ExcludeTeam: false, + } + res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, opts) assert.NoError(t, nErr) assert.Len(t, res.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type) assert.Equal(t, model.SidebarCategoryChannels, res.Categories[1].Type) assert.Equal(t, model.SidebarCategoryDirectMessages, res.Categories[2].Type) - res2, err := ss.Channel().GetSidebarCategories(userId, teamId2) + res2, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId2) assert.NoError(t, err) assert.Equal(t, res, res2) }) @@ -93,20 +109,24 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - initialCategories, err := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Equal(t, res, initialCategories) // Calling CreateInitialSidebarCategories a second time shouldn't create any new categories - res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, teamId) + res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, opts) assert.NoError(t, nErr) assert.NotEmpty(t, res) - res, err = ss.Channel().GetSidebarCategories(userId, teamId) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) assert.NoError(t, err) assert.Equal(t, initialCategories.Categories, res.Categories) }) @@ -123,13 +143,17 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { go func() { defer wg.Done() - _, _ = ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + _, _ = ss.Channel().CreateInitialSidebarCategories(userId, opts) }() } wg.Wait() - res, err := ss.Channel().GetSidebarCategories(userId, teamId) + res, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) assert.NoError(t, err) assert.Len(t, res.Categories, 3) }) @@ -176,7 +200,11 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { require.NoError(t, nErr) // Create the categories - categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.Len(t, categories.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, categories.Categories[0].Type) @@ -185,7 +213,7 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { assert.Equal(t, []string{channel2.Id}, categories.Categories[1].Channels) // Get and check the categories for channels - categories2, nErr := ss.Channel().GetSidebarCategories(userId, teamId) + categories2, nErr := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, nErr) require.Equal(t, categories, categories2) }) @@ -240,14 +268,18 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { require.NoError(t, nErr) // Create the categories - categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.Len(t, categories.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, categories.Categories[0].Type) assert.Equal(t, []string{channel2.Id, channel1.Id}, categories.Categories[0].Channels) // Get and check the categories for channels - categories2, nErr := ss.Channel().GetSidebarCategories(userId, teamId) + categories2, nErr := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, nErr) require.Equal(t, categories, categories2) }) @@ -303,7 +335,11 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { require.NoError(t, err) // Create the categories - categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.Len(t, categories.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, categories.Categories[0].Type) @@ -312,7 +348,7 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { assert.Equal(t, []string{dmChannel2.Id}, categories.Categories[2].Channels) // Get and check the categories for channels - categories2, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories2, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Equal(t, categories, categories2) }) @@ -347,7 +383,11 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { require.NoError(t, nErr) // Create the categories - categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + categories, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.Len(t, categories.Categories, 3) assert.Equal(t, model.SidebarCategoryFavorites, categories.Categories[0].Type) @@ -356,10 +396,54 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) { assert.Equal(t, []string{}, categories.Categories[1].Channels) // Get and check the categories for channels - categories2, nErr := ss.Channel().GetSidebarCategories(userId, teamId) + categories2, nErr := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, nErr) require.Equal(t, categories, categories2) }) + + t.Run("graphQL path to create initial favorites/channels/DMs categories on different teams", func(t *testing.T) { + userId := model.NewId() + + t1 := &model.Team{ + DisplayName: "DisplayName", + Name: NewTestId(), + Email: MakeEmail(), + Type: model.TeamOpen, + InviteId: model.NewId(), + } + t1, err := ss.Team().Save(t1) + require.NoError(t, err) + + m1 := &model.TeamMember{TeamId: t1.Id, UserId: userId} + _, nErr := ss.Team().SaveMember(m1, -1) + require.NoError(t, nErr) + + t2 := &model.Team{ + DisplayName: "DisplayName2", + Name: NewTestId(), + Email: MakeEmail(), + Type: model.TeamOpen, + InviteId: model.NewId(), + } + t2, err = ss.Team().Save(t2) + require.NoError(t, err) + + m2 := &model.TeamMember{TeamId: t2.Id, UserId: userId} + _, nErr = ss.Team().SaveMember(m2, -1) + require.NoError(t, nErr) + + opts := &store.SidebarCategorySearchOpts{ + TeamID: t1.Id, + ExcludeTeam: true, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) + require.NoError(t, nErr) + require.NotEmpty(t, res) + + for _, cat := range res.Categories { + assert.Equal(t, t2.Id, cat.TeamId) + } + }) } func testCreateSidebarCategory(t *testing.T, ss store.Store) { @@ -383,7 +467,11 @@ func testCreateSidebarCategory(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) @@ -396,7 +484,7 @@ func testCreateSidebarCategory(t *testing.T, ss store.Store) { require.NoError(t, err) // Confirm that it comes second - res, err = ss.Channel().GetSidebarCategories(userId, teamId) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 4) assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type) @@ -408,12 +496,16 @@ func testCreateSidebarCategory(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) // Re-arrange the categories so that Favorites comes last - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, categories.Categories, 3) require.Equal(t, model.SidebarCategoryFavorites, categories.Categories[0].Type) @@ -434,7 +526,7 @@ func testCreateSidebarCategory(t *testing.T, ss store.Store) { require.NoError(t, err) // Confirm that it comes first - res, err = ss.Channel().GetSidebarCategories(userId, teamId) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 4) assert.Equal(t, model.SidebarCategoryCustom, res.Categories[0].Type) @@ -445,7 +537,11 @@ func testCreateSidebarCategory(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) @@ -483,11 +579,15 @@ func testCreateSidebarCategory(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, categories.Categories, 3) @@ -549,7 +649,11 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { channelId2 := model.NewId() channelId3 := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) @@ -579,11 +683,15 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { teamId := model.NewId() // Create the initial categories and find the channels category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) channelsCategory := categories.Categories[1] @@ -643,11 +751,15 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { teamId := model.NewId() // Create the initial categories and find the channels category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Equal(t, model.SidebarCategoryChannels, categories.Categories[1].Type) @@ -681,12 +793,16 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { userId := model.NewId() teamId := model.NewId() + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } // Create the initial categories and find the channels category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := categories.Categories[0] @@ -745,11 +861,15 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { teamId := model.NewId() // Create the initial categories and find the DMs category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Equal(t, model.SidebarCategoryDirectMessages, categories.Categories[2].Type) @@ -786,11 +906,15 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { teamId := model.NewId() // Create the initial categories and find the DMs category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Equal(t, model.SidebarCategoryDirectMessages, categories.Categories[2].Type) @@ -824,11 +948,15 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { teamId := model.NewId() // Create the initial categories and find the DMs category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Equal(t, model.SidebarCategoryDirectMessages, categories.Categories[2].Type) @@ -854,8 +982,11 @@ func testGetSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { // Create another team and assign the DM to a custom category on that team otherTeamId := model.NewId() - - res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, otherTeamId) + opts = &store.SidebarCategorySearchOpts{ + TeamID: otherTeamId, + ExcludeTeam: false, + } + res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) @@ -882,7 +1013,11 @@ func testGetSidebarCategories(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) @@ -901,7 +1036,7 @@ func testGetSidebarCategories(t *testing.T, ss store.Store) { gotCategory, err := ss.Channel().GetSidebarCategory(newCategory.Id) require.NoError(t, err) - res, err = ss.Channel().GetSidebarCategories(userId, teamId) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 4) @@ -920,11 +1055,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { teamId := model.NewId() // Create the initial categories - res, err := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, err := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, err) require.NotEmpty(t, res) - initialCategories, err := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := initialCategories.Categories[0] @@ -954,11 +1093,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { teamId := model.NewId() // Create the initial categories - res, err := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, err := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, err) require.NotEmpty(t, res) - initialCategories, err := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := initialCategories.Categories[0] @@ -981,11 +1124,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - initialCategories, err := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := initialCategories.Categories[0] @@ -1048,11 +1195,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { teamId := model.NewId() // Create the initial categories and find the favorites category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := categories.Categories[0] @@ -1109,11 +1260,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { teamId := model.NewId() // Create the initial categories and find the favorites category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := categories.Categories[0] @@ -1176,21 +1331,29 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { teamId2 := model.NewId() // Create the initial categories and find the favorites categories in each team - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := categories.Categories[0] require.Equal(t, model.SidebarCategoryFavorites, favoritesCategory.Type) - res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, teamId2) + opts = &store.SidebarCategorySearchOpts{ + TeamID: teamId2, + ExcludeTeam: false, + } + res, nErr = ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories2, err := ss.Channel().GetSidebarCategories(userId, teamId2) + categories2, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId2) require.NoError(t, err) favoritesCategory2 := categories2.Categories[0] @@ -1276,11 +1439,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { teamId := model.NewId() // Create the initial categories and find the favorites category - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories, err := ss.Channel().GetSidebarCategories(userId, teamId) + categories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) favoritesCategory := categories.Categories[0] @@ -1291,11 +1458,11 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { // Create the other users' categories userId2 := model.NewId() - res, nErr = ss.Channel().CreateInitialSidebarCategories(userId2, teamId) + res, nErr = ss.Channel().CreateInitialSidebarCategories(userId2, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - categories2, err := ss.Channel().GetSidebarCategories(userId2, teamId) + categories2, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId2, teamId) require.NoError(t, err) favoritesCategory2 := categories2.Categories[0] @@ -1447,12 +1614,16 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { ) require.NoError(t, nErr) - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) // And some categories - initialCategories, nErr := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, nErr := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, nErr) channelsCategory := initialCategories.Categories[1] @@ -1502,12 +1673,16 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { ) require.NoError(t, nErr) - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) // The DM should start in the DMs category - initialCategories, err := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) dmsCategory := initialCategories.Categories[2] @@ -1590,11 +1765,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { require.NoError(t, err) // And then create the initial categories so that it includes the channel - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - initialCategories, nErr := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, nErr := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, nErr) channelsCategory := initialCategories.Categories[1] @@ -1654,11 +1833,15 @@ func testUpdateSidebarCategories(t *testing.T, ss store.Store) { require.NoError(t, err) // And then create the initial categories so that Channels includes the channel - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - initialCategories, nErr := ss.Channel().GetSidebarCategories(userId, teamId) + initialCategories, nErr := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, nErr) channelsCategory := initialCategories.Categories[1] @@ -1712,11 +1895,15 @@ func setupInitialSidebarCategories(t *testing.T, ss store.Store) (string, string userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) - res, err := ss.Channel().GetSidebarCategories(userId, teamId) + res, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 3) @@ -1835,11 +2022,15 @@ func testClearSidebarOnTeamLeave(t *testing.T, ss store.Store, s SqlStore) { // Create a second team and set up the sidebar categories for it teamId2 := model.NewId() - res, err := ss.Channel().CreateInitialSidebarCategories(userId, teamId2) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId2, + ExcludeTeam: false, + } + res, err := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, err) require.NotEmpty(t, res) - res, err = ss.Channel().GetSidebarCategories(userId, teamId2) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId2) require.NoError(t, err) require.Len(t, res.Categories, 3) @@ -1896,7 +2087,7 @@ func testClearSidebarOnTeamLeave(t *testing.T, ss store.Store, s SqlStore) { assert.Equal(t, int64(2), count) // Confirm that the categories on the second team are unchanged - res, err = ss.Channel().GetSidebarCategories(userId, teamId2) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId2) require.NoError(t, err) assert.Len(t, res.Categories, 4) @@ -1915,7 +2106,7 @@ func testDeleteSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { require.NotNil(t, newCategory) // Ensure that the category was created properly - res, err := ss.Channel().GetSidebarCategories(userId, teamId) + res, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 4) @@ -1923,7 +2114,7 @@ func testDeleteSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { err = ss.Channel().DeleteSidebarCategory(newCategory.Id) assert.NoError(t, err) - res, err = ss.Channel().GetSidebarCategories(userId, teamId) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 3) }) @@ -1967,7 +2158,7 @@ func testDeleteSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { require.NotNil(t, newCategory) // Ensure that the categories are set up correctly - res, err := ss.Channel().GetSidebarCategories(userId, teamId) + res, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 4) @@ -1979,7 +2170,7 @@ func testDeleteSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { assert.NoError(t, err) // Confirm that the category was deleted... - res, err = ss.Channel().GetSidebarCategories(userId, teamId) + res, err = ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) assert.NoError(t, err) assert.Len(t, res.Categories, 3) @@ -1999,7 +2190,7 @@ func testDeleteSidebarCategory(t *testing.T, ss store.Store, s SqlStore) { t.Run("should not allow you to remove non-custom categories", func(t *testing.T) { userId, teamId := setupInitialSidebarCategories(t, ss) defer ss.User().PermanentDelete(userId) - res, err := ss.Channel().GetSidebarCategories(userId, teamId) + res, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userId, teamId) require.NoError(t, err) require.Len(t, res.Categories, 3) require.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type) @@ -2022,7 +2213,11 @@ func testUpdateSidebarChannelsByPreferences(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) require.NoError(t, nErr) require.NotEmpty(t, res) @@ -2047,7 +2242,11 @@ func testUpdateSidebarChannelsByPreferences(t *testing.T, ss store.Store) { userId := model.NewId() teamId := model.NewId() - res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, teamId) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamId, + ExcludeTeam: false, + } + res, nErr := ss.Channel().CreateInitialSidebarCategories(userId, opts) assert.NoError(t, nErr) require.NotEmpty(t, res) @@ -2085,11 +2284,15 @@ func testSidebarCategoryDeadlock(t *testing.T, ss store.Store) { require.NoError(t, err) // And then create the initial categories so that it includes the channel - res, err := ss.Channel().CreateInitialSidebarCategories(userID, teamID) + opts := &store.SidebarCategorySearchOpts{ + TeamID: teamID, + ExcludeTeam: false, + } + res, err := ss.Channel().CreateInitialSidebarCategories(userID, opts) require.NoError(t, err) require.NotEmpty(t, res) - initialCategories, err := ss.Channel().GetSidebarCategories(userID, teamID) + initialCategories, err := ss.Channel().GetSidebarCategoriesForTeamForUser(userID, teamID) require.NoError(t, err) channelsCategory := initialCategories.Categories[1] diff --git a/store/storetest/mocks/ChannelStore.go b/store/storetest/mocks/ChannelStore.go index 070706699f..9bc9594d49 100644 --- a/store/storetest/mocks/ChannelStore.go +++ b/store/storetest/mocks/ChannelStore.go @@ -222,13 +222,13 @@ func (_m *ChannelStore) CreateDirectChannel(userID *model.User, otherUserID *mod return r0, r1 } -// CreateInitialSidebarCategories provides a mock function with given fields: userID, teamID -func (_m *ChannelStore) CreateInitialSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { - ret := _m.Called(userID, teamID) +// CreateInitialSidebarCategories provides a mock function with given fields: userID, opts +func (_m *ChannelStore) CreateInitialSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { + ret := _m.Called(userID, opts) var r0 *model.OrderedSidebarCategories - if rf, ok := ret.Get(0).(func(string, string) *model.OrderedSidebarCategories); ok { - r0 = rf(userID, teamID) + if rf, ok := ret.Get(0).(func(string, *store.SidebarCategorySearchOpts) *model.OrderedSidebarCategories); ok { + r0 = rf(userID, opts) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.OrderedSidebarCategories) @@ -236,8 +236,8 @@ func (_m *ChannelStore) CreateInitialSidebarCategories(userID string, teamID str } var r1 error - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(userID, teamID) + if rf, ok := ret.Get(1).(func(string, *store.SidebarCategorySearchOpts) error); ok { + r1 = rf(userID, opts) } else { r1 = ret.Error(1) } @@ -1372,8 +1372,31 @@ func (_m *ChannelStore) GetPublicChannelsForTeam(teamID string, offset int, limi return r0, r1 } -// GetSidebarCategories provides a mock function with given fields: userID, teamID -func (_m *ChannelStore) GetSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { +// GetSidebarCategories provides a mock function with given fields: userID, opts +func (_m *ChannelStore) GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { + ret := _m.Called(userID, opts) + + var r0 *model.OrderedSidebarCategories + if rf, ok := ret.Get(0).(func(string, *store.SidebarCategorySearchOpts) *model.OrderedSidebarCategories); ok { + r0 = rf(userID, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.OrderedSidebarCategories) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, *store.SidebarCategorySearchOpts) error); ok { + r1 = rf(userID, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetSidebarCategoriesForTeamForUser provides a mock function with given fields: userID, teamID +func (_m *ChannelStore) GetSidebarCategoriesForTeamForUser(userID string, teamID string) (*model.OrderedSidebarCategories, error) { ret := _m.Called(userID, teamID) var r0 *model.OrderedSidebarCategories diff --git a/store/timerlayer/timerlayer.go b/store/timerlayer/timerlayer.go index e34512b464..cc3178473c 100644 --- a/store/timerlayer/timerlayer.go +++ b/store/timerlayer/timerlayer.go @@ -677,10 +677,10 @@ func (s *TimerLayerChannelStore) CreateDirectChannel(userID *model.User, otherUs return result, err } -func (s *TimerLayerChannelStore) CreateInitialSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { +func (s *TimerLayerChannelStore) CreateInitialSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { start := time.Now() - result, err := s.ChannelStore.CreateInitialSidebarCategories(userID, teamID) + result, err := s.ChannelStore.CreateInitialSidebarCategories(userID, opts) elapsed := float64(time.Since(start)) / float64(time.Second) if s.Root.Metrics != nil { @@ -1509,10 +1509,10 @@ func (s *TimerLayerChannelStore) GetPublicChannelsForTeam(teamID string, offset return result, err } -func (s *TimerLayerChannelStore) GetSidebarCategories(userID string, teamID string) (*model.OrderedSidebarCategories, error) { +func (s *TimerLayerChannelStore) GetSidebarCategories(userID string, opts *store.SidebarCategorySearchOpts) (*model.OrderedSidebarCategories, error) { start := time.Now() - result, err := s.ChannelStore.GetSidebarCategories(userID, teamID) + result, err := s.ChannelStore.GetSidebarCategories(userID, opts) elapsed := float64(time.Since(start)) / float64(time.Second) if s.Root.Metrics != nil { @@ -1525,6 +1525,22 @@ func (s *TimerLayerChannelStore) GetSidebarCategories(userID string, teamID stri return result, err } +func (s *TimerLayerChannelStore) GetSidebarCategoriesForTeamForUser(userID string, teamID string) (*model.OrderedSidebarCategories, error) { + start := time.Now() + + result, err := s.ChannelStore.GetSidebarCategoriesForTeamForUser(userID, teamID) + + elapsed := float64(time.Since(start)) / float64(time.Second) + if s.Root.Metrics != nil { + success := "false" + if err == nil { + success = "true" + } + s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.GetSidebarCategoriesForTeamForUser", success, elapsed) + } + return result, err +} + func (s *TimerLayerChannelStore) GetSidebarCategory(categoryID string) (*model.SidebarCategoryWithChannels, error) { start := time.Now()