diff --git a/server/Makefile b/server/Makefile
index bb2c437a12..1023eb21bb 100644
--- a/server/Makefile
+++ b/server/Makefile
@@ -50,10 +50,6 @@ ifeq ($(BUILD_NUMBER),)
BUILD_NUMBER := dev
endif
-ifeq ($(BUILD_NUMBER),dev)
- export MM_FEATUREFLAGS_GRAPHQL = true
-endif
-
# Ensure developer invocation and tests are anchored.
MM_SERVER_PATH ?= $(ROOT)
diff --git a/server/channels/api4/api.go b/server/channels/api4/api.go
index d20fdd50fb..5275df3b4b 100644
--- a/server/channels/api4/api.go
+++ b/server/channels/api4/api.go
@@ -7,7 +7,6 @@ import (
"net/http"
"github.com/gorilla/mux"
- graphql "github.com/graph-gophers/graphql-go"
_ "github.com/mattermost/go-i18n/i18n"
"github.com/mattermost/mattermost/server/public/model"
@@ -142,7 +141,6 @@ type Routes struct {
type API struct {
srv *app.Server
- schema *graphql.Schema
BaseRoutes *Routes
}
@@ -306,9 +304,6 @@ func Init(srv *app.Server) (*API, error) {
api.InitUsage()
api.InitHostedCustomer()
api.InitDrafts()
- if err := api.InitGraphQL(); err != nil {
- return nil, err
- }
srv.Router.Handle("/api/v4/{anything:.*}", http.HandlerFunc(api.Handle404))
diff --git a/server/channels/api4/apitestlib.go b/server/channels/api4/apitestlib.go
index 449d4c6208..4e56583c84 100644
--- a/server/channels/api4/apitestlib.go
+++ b/server/channels/api4/apitestlib.go
@@ -4,9 +4,7 @@
package api4
import (
- "bytes"
"context"
- "encoding/json"
"errors"
"fmt"
"io"
@@ -21,7 +19,6 @@ import (
"time"
"github.com/gorilla/websocket"
- graphql "github.com/graph-gophers/graphql-go"
s3 "github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/stretchr/testify/require"
@@ -47,7 +44,6 @@ type TestHelper struct {
Context *request.Context
Client *model.Client4
- GraphQLClient *graphQLClient
BasicUser *model.User
BasicUser2 *model.User
TeamAdminUser *model.User
@@ -196,7 +192,6 @@ func setupTestHelper(dbStore store.Store, searchEngine *searchengine.Broker, ent
}
th.Client = th.CreateClient()
- th.GraphQLClient = newGraphQLClient(fmt.Sprintf("http://localhost:%v", th.App.Srv().ListenAddr.Port))
th.SystemAdminClient = th.CreateClient()
th.SystemManagerClient = th.CreateClient()
@@ -811,16 +806,10 @@ func (th *TestHelper) CreateDmChannel(user *model.User) *model.Channel {
func (th *TestHelper) LoginBasic() {
th.LoginBasicWithClient(th.Client)
- if os.Getenv("MM_FEATUREFLAGS_GRAPHQL") == "true" {
- th.LoginBasicWithGraphQL()
- }
}
func (th *TestHelper) LoginBasic2() {
th.LoginBasic2WithClient(th.Client)
- if os.Getenv("MM_FEATUREFLAGS_GRAPHQL") == "true" {
- th.LoginBasicWithGraphQL()
- }
}
func (th *TestHelper) LoginTeamAdmin() {
@@ -842,13 +831,6 @@ func (th *TestHelper) LoginBasicWithClient(client *model.Client4) {
}
}
-func (th *TestHelper) LoginBasicWithGraphQL() {
- _, _, err := th.GraphQLClient.login(th.BasicUser.Email, th.BasicUser.Password)
- if err != nil {
- panic(err)
- }
-}
-
func (th *TestHelper) LoginBasic2WithClient(client *model.Client4) {
_, _, err := client.Login(context.Background(), th.BasicUser2.Email, th.BasicUser2.Password)
if err != nil {
@@ -1301,22 +1283,3 @@ func (th *TestHelper) SetupScheme(scope string) *model.Scheme {
}
return scheme
}
-
-func (th *TestHelper) MakeGraphQLRequest(input *graphQLInput) (*graphql.Response, error) {
- url := fmt.Sprintf("http://localhost:%v", th.App.Srv().ListenAddr.Port) + model.APIURLSuffixV5 + "/graphql"
-
- buf, err := json.Marshal(input)
- if err != nil {
- panic(err)
- }
-
- resp, err := th.GraphQLClient.doAPIRequest("POST", url, bytes.NewReader(buf), map[string]string{})
- if err != nil {
- panic(err)
- }
- defer closeBody(resp)
-
- var gqlResp *graphql.Response
- err = json.NewDecoder(resp.Body).Decode(&gqlResp)
- return gqlResp, err
-}
diff --git a/server/channels/api4/graphql.go b/server/channels/api4/graphql.go
deleted file mode 100644
index 6212c08cdd..0000000000
--- a/server/channels/api4/graphql.go
+++ /dev/null
@@ -1,193 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- _ "embed"
- "encoding/json"
- "net/http"
-
- "github.com/graph-gophers/dataloader/v6"
- graphql "github.com/graph-gophers/graphql-go"
- gqlerrors "github.com/graph-gophers/graphql-go/errors"
-
- "github.com/mattermost/mattermost/server/public/model"
- "github.com/mattermost/mattermost/server/public/shared/mlog"
- "github.com/mattermost/mattermost/server/v8/channels/web"
-)
-
-type graphQLInput struct {
- Query string `json:"query"`
- OperationName string `json:"operationName"`
- Variables map[string]any `json:"variables"`
-}
-
-// Unique type to hold our context.
-type ctxKey int
-
-const (
- webCtx ctxKey = 0
- rolesLoaderCtx ctxKey = 1
- channelsLoaderCtx ctxKey = 2
- teamsLoaderCtx ctxKey = 3
- usersLoaderCtx ctxKey = 4
-)
-
-const loaderBatchCapacity = web.PerPageMaximum
-
-//go:embed schema.graphqls
-var schemaRaw string
-
-func (api *API) InitGraphQL() error {
- // Guard with a feature flag.
- if !api.srv.Config().FeatureFlags.GraphQL {
- return nil
- }
-
- var err error
- opts := []graphql.SchemaOpt{
- graphql.UseFieldResolvers(),
- graphql.Logger(mlog.NewGraphQLLogger(api.srv.Log())),
- graphql.MaxParallelism(loaderBatchCapacity), // This is dangerous if the query
- // uses any non-dataloader backed object. So we need to be a bit careful here.
- }
-
- if isProd() {
- opts = append(opts,
- // MaxDepth cannot be moved as a general param
- // because otherwise introspection also doesn't work
- // with just a depth of 4.
- graphql.MaxDepth(4),
- graphql.DisableIntrospection(),
- )
- }
-
- api.schema, err = graphql.ParseSchema(schemaRaw, &resolver{}, opts...)
- if err != nil {
- return err
- }
-
- api.BaseRoutes.APIRoot5.Handle("/graphql", api.APIHandlerTrustRequester(graphiQL)).Methods("GET")
- api.BaseRoutes.APIRoot5.Handle("/graphql", api.APISessionRequired(api.graphQL)).Methods("POST")
- return nil
-}
-
-func (api *API) graphQL(c *Context, w http.ResponseWriter, r *http.Request) {
- var response *graphql.Response
- defer func() {
- if response != nil {
- if err := json.NewEncoder(w).Encode(response); err != nil {
- c.Logger.Warn("Error while writing response", mlog.Err(err))
- }
- }
- }()
-
- // Limit bodies to 100KiB.
- // We need to enforce a lower limit than the file upload size,
- // to prevent the library doing unnecessary parsing.
- r.Body = http.MaxBytesReader(w, r.Body, 102400)
-
- var params graphQLInput
- if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil {
- err2 := gqlerrors.Errorf("invalid request body: %v", err)
- response = &graphql.Response{Errors: []*gqlerrors.QueryError{err2}}
- return
- }
-
- if isProd() && params.OperationName == "" {
- err2 := gqlerrors.Errorf("operation name not passed")
- response = &graphql.Response{Errors: []*gqlerrors.QueryError{err2}}
- return
- }
-
- c.GraphQLOperationName = params.OperationName
-
- // Populate the context with required info.
- reqCtx := r.Context()
- reqCtx = context.WithValue(reqCtx, webCtx, c)
-
- rolesLoader := dataloader.NewBatchedLoader(graphQLRolesLoader, dataloader.WithBatchCapacity(loaderBatchCapacity))
- reqCtx = context.WithValue(reqCtx, rolesLoaderCtx, rolesLoader)
-
- channelsLoader := dataloader.NewBatchedLoader(graphQLChannelsLoader, dataloader.WithBatchCapacity(loaderBatchCapacity))
- reqCtx = context.WithValue(reqCtx, channelsLoaderCtx, channelsLoader)
-
- teamsLoader := dataloader.NewBatchedLoader(graphQLTeamsLoader, dataloader.WithBatchCapacity(loaderBatchCapacity))
- reqCtx = context.WithValue(reqCtx, teamsLoaderCtx, teamsLoader)
-
- usersLoader := dataloader.NewBatchedLoader(graphQLUsersLoader, dataloader.WithBatchCapacity(loaderBatchCapacity))
- reqCtx = context.WithValue(reqCtx, usersLoaderCtx, usersLoader)
-
- response = api.schema.Exec(reqCtx,
- params.Query,
- params.OperationName,
- params.Variables)
-
- if len(response.Errors) > 0 {
- logFunc := mlog.Error
- for _, gqlErr := range response.Errors {
- if gqlErr.Err != nil {
- if appErr, ok := gqlErr.Err.(*model.AppError); ok && appErr.StatusCode < http.StatusInternalServerError {
- logFunc = mlog.Debug
- break
- }
- }
- }
- logFunc("Error executing request", mlog.String("operation", params.OperationName),
- mlog.Array("errors", response.Errors))
- }
-}
-
-func graphiQL(c *Context, w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/html")
- w.Write(graphiqlPage)
-}
-
-var graphiqlPage = []byte(`
-
-
-
- GraphiQL editor | Mattermost
-
-
-
-
-
-
-
-
- Loading...
-
-
-
-`)
-
-// isProd is a helper function to apply prod-specific graphQL validations.
-func isProd() bool {
- return model.BuildNumber != "dev"
-}
diff --git a/server/channels/api4/graphql_client.go b/server/channels/api4/graphql_client.go
deleted file mode 100644
index 975d350aee..0000000000
--- a/server/channels/api4/graphql_client.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "encoding/json"
- "io"
- "net/http"
- "strings"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-// graphQLClient is an internal test client to run the tests.
-// When the API matures, we will expose it to the model package.
-type graphQLClient struct {
- URL string // The location of the server, for example "http://localhost:8065"
- APIURL string // The api location of the server, for example "http://localhost:8065/api/v4"
- httpClient *http.Client // The http client
- authToken string
- authType string
- httpHeader map[string]string // Headers to be copied over for each request
-}
-
-func newGraphQLClient(url string) *graphQLClient {
- url = strings.TrimRight(url, "/")
- return &graphQLClient{url, url + model.APIURLSuffix, &http.Client{}, "", "", map[string]string{}}
-}
-
-func (c *graphQLClient) login(loginId string, password string) (*model.User, *model.Response, error) {
- m := make(map[string]string)
- m["login_id"] = loginId
- m["password"] = password
-
- r, err := c.doAPIRequest(http.MethodPost, c.APIURL+"/users/login", strings.NewReader(model.MapToJSON(m)), map[string]string{model.HeaderEtagClient: ""})
-
- if err != nil {
- return nil, model.BuildResponse(r), err
- }
- defer closeBody(r)
- c.authToken = r.Header.Get(model.HeaderToken)
- c.authType = model.HeaderBearer
-
- var user model.User
- if jsonErr := json.NewDecoder(r.Body).Decode(&user); jsonErr != nil {
- return nil, nil, model.NewAppError("login", "api.unmarshal_error", nil, "", http.StatusInternalServerError).Wrap(jsonErr)
- }
- return &user, model.BuildResponse(r), nil
-}
-
-func (c *graphQLClient) doAPIRequest(method, url string, data io.Reader, headers map[string]string) (*http.Response, error) {
- rq, err := c.prepareRequest(method, url, data, headers)
- if err != nil {
- return nil, err
- }
-
- rp, err := c.httpClient.Do(rq)
- if err != nil {
- return rp, err
- }
-
- return rp, nil
-}
-
-func (c *graphQLClient) prepareRequest(method, url string, data io.Reader, headers map[string]string) (*http.Request, error) {
- rq, err := http.NewRequest(method, url, data)
- if err != nil {
- return nil, err
- }
-
- for k, v := range headers {
- rq.Header.Set(k, v)
- }
-
- if c.authToken != "" {
- rq.Header.Set(model.HeaderAuth, c.authType+" "+c.authToken)
- }
-
- if c.httpHeader != nil && len(c.httpHeader) > 0 {
- for k, v := range c.httpHeader {
- rq.Header.Set(k, v)
- }
- }
-
- return rq, nil
-}
diff --git a/server/channels/api4/graphql_test.go b/server/channels/api4/graphql_test.go
deleted file mode 100644
index 2f24f1b4d2..0000000000
--- a/server/channels/api4/graphql_test.go
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "os"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestGraphQLPayload(t *testing.T) {
- os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
- defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
-
- th := Setup(t).InitBasic()
- defer th.TearDown()
-
- largeString := strings.Repeat("hello", 204800)
-
- input := graphQLInput{
- OperationName: "config",
- Query: largeString,
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 1)
- // The actual error isn't exposed. We compare the string
- // to not confuse with other errors.
- require.Contains(t, resp.Errors[0].Message, "request body too large")
-}
diff --git a/server/channels/api4/resolver.go b/server/channels/api4/resolver.go
deleted file mode 100644
index bdd1b79329..0000000000
--- a/server/channels/api4/resolver.go
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/graph-gophers/dataloader/v6"
-
- "github.com/mattermost/mattermost/server/public/model"
- "github.com/mattermost/mattermost/server/v8/channels/app"
- "github.com/mattermost/mattermost/server/v8/channels/store"
- "github.com/mattermost/mattermost/server/v8/channels/web"
-)
-
-// cursorPrefix is used to categorize objects
-// sent in a cursor. The type is prepended
-// to the string with a - to find which
-// object the id belongs to.
-//
-// And after the type is extracted, object
-// specific logic can be applied to extract the id.
-type cursorPrefix string
-
-const (
- channelMemberCursorPrefix cursorPrefix = "channelMember"
- channelCursorPrefix cursorPrefix = "channel"
-)
-
-type resolver struct {
-}
-
-// match with api4.getChannelsForTeamForUser
-func (r *resolver) Channels(ctx context.Context, args struct {
- TeamID string
- UserID string
- IncludeDeleted bool
- LastDeleteAt float64
- LastUpdateAt float64
- First int32
- After string
-}) ([]*channel, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if args.UserID == model.Me {
- args.UserID = c.AppContext.Session().UserId
- }
-
- if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), args.UserID) {
- c.SetPermissionError(model.PermissionEditOtherUsers)
- return nil, c.Err
- }
-
- if args.TeamID != "" && !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), args.TeamID, model.PermissionViewTeam) {
- c.SetPermissionError(model.PermissionViewTeam)
- return nil, c.Err
- }
-
- limit := int(args.First)
- // ensure args.First limit
- if limit == 0 {
- limit = web.PerPageDefault
- } else if limit > web.PerPageMaximum {
- return nil, fmt.Errorf("first parameter %d higher than allowed maximum of %d", limit, web.PerPageMaximum)
- }
-
- // ensure args.After format
- var afterChannel string
- var ok bool
- if args.After != "" {
- afterChannel, ok = parseChannelCursor(args.After)
- if !ok {
- return nil, fmt.Errorf("after cursor not in the correct format: %s", args.After)
- }
- }
-
- // TODO: convert this to a streaming API.
- channels, appErr := c.App.GetChannelsForTeamForUserWithCursor(c.AppContext, args.TeamID, args.UserID, &model.ChannelSearchOpts{
- IncludeDeleted: args.IncludeDeleted,
- LastDeleteAt: int(args.LastDeleteAt),
- LastUpdateAt: int(args.LastUpdateAt),
- PerPage: model.NewInt(limit),
- }, afterChannel)
- if appErr != nil {
- return nil, appErr
- }
-
- appErr = c.App.FillInChannelsProps(c.AppContext, channels)
- if appErr != nil {
- return nil, appErr
- }
-
- return postProcessChannels(c, channels)
-}
-
-// match with api4.getUser
-func (r *resolver) User(ctx context.Context, args struct{ ID string }) (*user, error) {
- return getGraphQLUser(ctx, args.ID)
-}
-
-// match with api4.getClientConfig
-func (r *resolver) Config(ctx context.Context) (model.StringMap, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if c.AppContext.Session().UserId == "" {
- return c.App.Srv().Platform().LimitedClientConfigWithComputed(), nil
- }
- return c.App.Srv().Platform().ClientConfigWithComputed(), nil
-}
-
-// match with api4.getClientLicense
-func (r *resolver) License(ctx context.Context) (model.StringMap, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionReadLicenseInformation) {
- return c.App.Srv().ClientLicense(), nil
- }
- return c.App.Srv().GetSanitizedClientLicense(), nil
-}
-
-// match with api4.getTeamMembersForUser for teamID=""
-// and api4.getTeamMember for teamID != ""
-func (r *resolver) TeamMembers(ctx context.Context, args struct {
- UserID string
- TeamID string
- ExcludeTeam bool
-}) ([]*teamMember, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if args.UserID == model.Me {
- args.UserID = c.AppContext.Session().UserId
- }
-
- if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), args.UserID) && !c.App.SessionHasPermissionTo(*c.AppContext.Session(), model.PermissionReadOtherUsersTeams) {
- c.SetPermissionError(model.PermissionReadOtherUsersTeams)
- return nil, c.Err
- }
-
- canSee, appErr := c.App.UserCanSeeOtherUser(c.AppContext, c.AppContext.Session().UserId, args.UserID)
- if appErr != nil {
- return nil, appErr
- }
-
- if !canSee {
- c.SetPermissionError(model.PermissionViewMembers)
- return nil, c.Err
- }
-
- if args.TeamID != "" && !args.ExcludeTeam {
- if !c.App.SessionHasPermissionToTeam(*c.AppContext.Session(), args.TeamID, model.PermissionViewTeam) {
- c.SetPermissionError(model.PermissionViewTeam)
- return nil, c.Err
- }
-
- tm, appErr2 := c.App.GetTeamMember(c.AppContext, args.TeamID, args.UserID)
- if appErr2 != nil {
- return nil, appErr2
- }
-
- return []*teamMember{{*tm}}, nil
- }
-
- excludeTeamID := ""
- if args.TeamID != "" && args.ExcludeTeam {
- excludeTeamID = args.TeamID
- }
-
- // Do not return archived team members
- members, appErr := c.App.GetTeamMembersForUser(c.AppContext, args.UserID, excludeTeamID, false)
- if appErr != nil {
- return nil, appErr
- }
-
- // Convert to the wrapper format.
- res := make([]*teamMember, 0, len(members))
- for _, tm := range members {
- res = append(res, &teamMember{*tm})
- }
-
- return res, nil
-}
-
-func (*resolver) ChannelsLeft(ctx context.Context, args struct {
- UserID string
- Since float64
-}) ([]string, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if args.UserID == model.Me {
- args.UserID = c.AppContext.Session().UserId
- }
-
- if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), args.UserID) {
- c.SetPermissionError(model.PermissionEditOtherUsers)
- return nil, c.Err
- }
-
- return c.App.Srv().Store().ChannelMemberHistory().GetChannelsLeftSince(args.UserID, int64(args.Since))
-}
-
-// match with api4.getChannelMember
-func (*resolver) ChannelMembers(ctx context.Context, args struct {
- UserID string
- TeamID string
- ChannelID string
- ExcludeTeam bool
- First int32
- After string
- LastUpdateAt float64
-}) ([]*channelMember, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if args.UserID == model.Me {
- args.UserID = c.AppContext.Session().UserId
- }
-
- // If it's a single channel
- if args.ChannelID != "" {
- if !c.App.SessionHasPermissionToChannel(c.AppContext, *c.AppContext.Session(), args.ChannelID, model.PermissionReadChannel) {
- c.SetPermissionError(model.PermissionReadChannel)
- return nil, c.Err
- }
-
- ctx := c.AppContext
- ctx.SetContext(app.WithMaster(ctx.Context()))
- member, appErr := c.App.GetChannelMember(ctx, args.ChannelID, args.UserID)
- if appErr != nil {
- return nil, appErr
- }
-
- return []*channelMember{{*member}}, nil
- }
-
- if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), args.UserID) {
- c.SetPermissionError(model.PermissionEditOtherUsers)
- return nil, c.Err
- }
-
- limit := int(args.First)
- // ensure args.First limit
- if limit == 0 {
- limit = web.PerPageDefault
- } else if limit > web.PerPageMaximum {
- return nil, fmt.Errorf("first parameter %d higher than allowed maximum of %d", limit, web.PerPageMaximum)
- }
-
- // ensure args.After format
- var afterChannel, afterUser string
- var ok bool
- if args.After != "" {
- afterChannel, afterUser, ok = parseChannelMemberCursor(args.After)
- if !ok {
- return nil, fmt.Errorf("after cursor not in the correct format: %s", args.After)
- }
- }
-
- if args.TeamID != "" {
- 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 []*channelMember{}, appErr
- }
- args.TeamID = team.Id
- } else {
- return []*channelMember{}, nil
- }
- }
- }
-
- opts := &store.ChannelMemberGraphQLSearchOpts{
- AfterChannel: afterChannel,
- AfterUser: afterUser,
- Limit: limit,
- LastUpdateAt: int(args.LastUpdateAt),
- ExcludeTeam: args.ExcludeTeam,
- }
- members, err := c.App.Srv().Store().Channel().GetMembersForUserWithCursor(args.UserID, args.TeamID, opts)
- if err != nil {
- return nil, err
- }
-
- res := make([]*channelMember, 0, len(members))
- for _, cm := range members {
- res = append(res, &channelMember{cm})
- }
-
- return res, nil
-}
-
-// match with api4.getCategoriesForTeamForUser
-func (*resolver) SidebarCategories(ctx context.Context, args struct {
- UserID string
- TeamID string
- ExcludeTeam bool
-}) ([]*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
- }
-
- 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(c.AppContext, 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(c.AppContext, 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.
-// Kind of an anti-pattern, but there are lots of methods attached to *web.Context
-// so we use it for now.
-func getCtx(ctx context.Context) (*web.Context, error) {
- c, ok := ctx.Value(webCtx).(*web.Context)
- if !ok {
- return nil, errors.New("no web.Context found in context")
- }
- return c, nil
-}
-
-// getRolesLoader returns the roles loader out of the context.
-func getRolesLoader(ctx context.Context) (*dataloader.Loader, error) {
- l, ok := ctx.Value(rolesLoaderCtx).(*dataloader.Loader)
- if !ok {
- return nil, errors.New("no dataloader.Loader found in context")
- }
- return l, nil
-}
-
-// getChannelsLoader returns the channels loader out of the context.
-func getChannelsLoader(ctx context.Context) (*dataloader.Loader, error) {
- l, ok := ctx.Value(channelsLoaderCtx).(*dataloader.Loader)
- if !ok {
- return nil, errors.New("no dataloader.Loader found in context")
- }
- return l, nil
-}
-
-// getTeamsLoader returns the teams loader out of the context.
-func getTeamsLoader(ctx context.Context) (*dataloader.Loader, error) {
- l, ok := ctx.Value(teamsLoaderCtx).(*dataloader.Loader)
- if !ok {
- return nil, errors.New("no dataloader.Loader found in context")
- }
- return l, nil
-}
-
-// getUsersLoader returns the users loader out of the context.
-func getUsersLoader(ctx context.Context) (*dataloader.Loader, error) {
- l, ok := ctx.Value(usersLoaderCtx).(*dataloader.Loader)
- if !ok {
- return nil, errors.New("no dataloader.Loader found in context")
- }
- return l, nil
-}
diff --git a/server/channels/api4/resolver_channel.go b/server/channels/api4/resolver_channel.go
deleted file mode 100644
index 6dbe080930..0000000000
--- a/server/channels/api4/resolver_channel.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "encoding/base64"
- "fmt"
- "sort"
- "strings"
-
- "github.com/mattermost/mattermost/server/public/model"
- "github.com/mattermost/mattermost/server/v8/channels/web"
-)
-
-// channel is an internal graphQL wrapper struct to add resolver methods.
-type channel struct {
- model.Channel
- PrettyDisplayName string
-}
-
-// match with api4.getTeam
-func (ch *channel) Team(ctx context.Context) (*model.Team, error) {
- if ch.TeamId == "" {
- return nil, nil
- }
-
- return getGraphQLTeam(ctx, ch.TeamId)
-}
-
-func (ch *channel) Cursor() *string {
- cursor := string(channelCursorPrefix) + "-" + ch.Id
- encoded := base64.StdEncoding.EncodeToString([]byte(cursor))
- return model.NewString(encoded)
-}
-
-func parseChannelCursor(cursor string) (channelID string, ok bool) {
- decoded, err := base64.StdEncoding.DecodeString(cursor)
- if err != nil {
- return "", false
- }
-
- prefix, id, found := strings.Cut(string(decoded), "-")
- if !found {
- return "", false
- }
-
- if cursorPrefix(prefix) != channelCursorPrefix {
- return "", false
- }
-
- return id, true
-}
-
-func postProcessChannels(c *web.Context, channels []*model.Channel) ([]*channel, error) {
- // This approach becomes effectively similar to a dataloader if the displayName computation
- // were to be done at the field level per channel.
-
- // Get DM/GM channelIDs and set empty maps as well.
- var channelIDs []string
- for _, ch := range channels {
- if ch.IsGroupOrDirect() {
- channelIDs = append(channelIDs, ch.Id)
- }
-
- // This is needed to avoid sending null, which
- // does not match with the schema since props is not nullable.
- // And making it nullable would mean taking pointer of a map,
- // which is not very idiomatic.
- ch.MakeNonNil()
- }
-
- var nameFormat string
- var userInfo map[string][]*model.User
- var err error
-
- // Avoiding unnecessary queries unless necessary.
- if len(channelIDs) > 0 {
- userInfo, err = c.App.Srv().Store().Channel().GetMembersInfoByChannelIds(channelIDs)
- if err != nil {
- return nil, err
- }
-
- user := &model.User{Id: c.AppContext.Session().UserId}
- nameFormat = c.App.GetNotificationNameFormat(user)
- }
-
- // Convert to the wrapper format.
- nameCache := make(map[string]string)
- res := make([]*channel, len(channels))
- for i, ch := range channels {
- prettyName := ch.DisplayName
-
- if ch.IsGroupOrDirect() {
- // get users slice for channel id
- users := userInfo[ch.Id]
- if users == nil {
- return nil, fmt.Errorf("user info not found for channel id: %s", ch.Id)
- }
- prettyName = getPrettyDNForUsers(nameFormat, users, c.AppContext.Session().UserId, nameCache)
- }
-
- res[i] = &channel{Channel: *ch, PrettyDisplayName: prettyName}
- }
-
- return res, nil
-}
-
-func getPrettyDNForUsers(displaySetting string, users []*model.User, omitUserId string, cache map[string]string) string {
- displayNames := make([]string, 0, len(users))
- for _, u := range users {
- if u.Id == omitUserId {
- continue
- }
- displayNames = append(displayNames, getPrettyDNForUser(displaySetting, u, cache))
- }
-
- sort.Strings(displayNames)
- result := strings.Join(displayNames, ", ")
- if result == "" {
- // Self DM
- result = getPrettyDNForUser(displaySetting, users[0], cache)
- }
- return result
-}
-
-func getPrettyDNForUser(displaySetting string, user *model.User, cache map[string]string) string {
- // use the cache first
- if name, ok := cache[user.Id]; ok {
- return name
- }
-
- var displayName string
- switch displaySetting {
- case "nickname_full_name":
- displayName = user.Nickname
- if strings.TrimSpace(displayName) == "" {
- displayName = user.GetFullName()
- }
- if strings.TrimSpace(displayName) == "" {
- displayName = user.Username
- }
- case "full_name":
- displayName = user.GetFullName()
- if strings.TrimSpace(displayName) == "" {
- displayName = user.Username
- }
- default: // the "username" case also falls under this one.
- displayName = user.Username
- }
-
- // update the cache
- cache[user.Id] = displayName
-
- return displayName
-}
diff --git a/server/channels/api4/resolver_channel_member.go b/server/channels/api4/resolver_channel_member.go
deleted file mode 100644
index c7e7018bd7..0000000000
--- a/server/channels/api4/resolver_channel_member.go
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "encoding/base64"
- "fmt"
- "strings"
-
- "github.com/graph-gophers/dataloader/v6"
-
- "github.com/mattermost/mattermost/server/public/model"
- "github.com/mattermost/mattermost/server/v8/channels/web"
-)
-
-// channelMember is an internal graphQL wrapper struct to add resolver methods.
-type channelMember struct {
- model.ChannelMember
-}
-
-// match with api4.getUser
-func (cm *channelMember) User(ctx context.Context) (*user, error) {
- return getGraphQLUser(ctx, cm.UserId)
-}
-
-// match with api4.Channel
-func (cm *channelMember) Channel(ctx context.Context) (*channel, error) {
- loader, err := getChannelsLoader(ctx)
- if err != nil {
- return nil, err
- }
-
- thunk := loader.Load(ctx, dataloader.StringKey(cm.ChannelId))
- result, err := thunk()
- if err != nil {
- return nil, err
- }
- channel := result.(*channel)
-
- return channel, nil
-}
-
-func graphQLChannelsLoader(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
- stringKeys := keys.Keys()
- result := make([]*dataloader.Result, len(stringKeys))
-
- c, err := getCtx(ctx)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- channels, err := getGraphQLChannels(c, stringKeys)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- for i, ch := range channels {
- result[i] = &dataloader.Result{Data: ch}
- }
- return result
-}
-
-func getGraphQLChannels(c *web.Context, channelIDs []string) ([]*channel, error) {
- channels, appErr := c.App.GetChannels(c.AppContext, channelIDs)
- if appErr != nil {
- return nil, appErr
- }
-
- if len(channels) != len(channelIDs) {
- return nil, fmt.Errorf("all channels were not found. Requested %d; Found %d", len(channelIDs), len(channels))
- }
-
- var openChannels, nonOpenChannels, teamsForOpenChannels []string
- uniqueTeams := make(map[string]bool)
- for _, ch := range channels {
- if ch.Type == model.ChannelTypeOpen {
- openChannels = append(openChannels, ch.Id)
- uniqueTeams[ch.TeamId] = true
- } else {
- nonOpenChannels = append(nonOpenChannels, ch.Id)
- }
- }
-
- for teamID := range uniqueTeams {
- teamsForOpenChannels = append(teamsForOpenChannels, teamID)
- }
-
- if len(openChannels) > 0 && !c.App.SessionHasPermissionToChannels(c.AppContext, *c.AppContext.Session(), openChannels, model.PermissionReadChannel) &&
- !c.App.SessionHasPermissionToTeams(c.AppContext, *c.AppContext.Session(), teamsForOpenChannels, model.PermissionReadPublicChannel) {
- c.SetPermissionError(model.PermissionReadPublicChannel)
- return nil, c.Err
- }
-
- if len(nonOpenChannels) > 0 && !c.App.SessionHasPermissionToChannels(c.AppContext, *c.AppContext.Session(), nonOpenChannels, model.PermissionReadChannel) {
- c.SetPermissionError(model.PermissionReadChannel)
- return nil, c.Err
- }
-
- appErr = c.App.FillInChannelsProps(c.AppContext, model.ChannelList(channels))
- if appErr != nil {
- return nil, appErr
- }
-
- res, err := postProcessChannels(c, channels)
- if err != nil {
- return nil, err
- }
-
- // The channels need to be in the exact same order as the input slice.
- tmp := make(map[string]*channel)
- for _, ch := range res {
- tmp[ch.Id] = ch
- }
-
- // We reuse the same slice and just rewrite the channels.
- for i, id := range channelIDs {
- res[i] = tmp[id]
- }
-
- return res, nil
-}
-
-func (cm *channelMember) Roles_(ctx context.Context) ([]*model.Role, error) {
- loader, err := getRolesLoader(ctx)
- if err != nil {
- return nil, err
- }
-
- thunk := loader.LoadMany(ctx, dataloader.NewKeysFromStrings(strings.Fields(cm.Roles)))
- results, errs := thunk()
- // All errors are the same. We just return the first one.
- if len(errs) > 0 && errs[0] != nil {
- return nil, err
- }
-
- roles := make([]*model.Role, len(results))
- for i, res := range results {
- roles[i] = res.(*model.Role)
- }
-
- return roles, nil
-}
-
-func (cm *channelMember) Cursor() *string {
- cursor := string(channelMemberCursorPrefix) + "-" + cm.ChannelId + "-" + cm.UserId
- encoded := base64.StdEncoding.EncodeToString([]byte(cursor))
- return model.NewString(encoded)
-}
-
-func graphQLRolesLoader(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
- stringKeys := keys.Keys()
- result := make([]*dataloader.Result, len(stringKeys))
-
- c, err := getCtx(ctx)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- roles, err := getGraphQLRoles(c, stringKeys)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- for i, role := range roles {
- result[i] = &dataloader.Result{Data: role}
- }
- return result
-}
-
-func getGraphQLRoles(c *web.Context, roleNames []string) ([]*model.Role, error) {
- cleanedRoleNames, valid := model.CleanRoleNames(roleNames)
- if !valid {
- c.SetInvalidParam("rolename")
- return nil, c.Err
- }
-
- roles, appErr := c.App.GetRolesByNames(cleanedRoleNames)
- if appErr != nil {
- return nil, appErr
- }
-
- // The roles need to be in the exact same order as the input slice.
- tmp := make(map[string]*model.Role)
- for _, r := range roles {
- tmp[r.Name] = r
- }
-
- // We reuse the same slice and just rewrite the roles.
- for i, roleName := range roleNames {
- roles[i] = tmp[roleName]
- }
-
- return roles, nil
-}
-
-func parseChannelMemberCursor(cursor string) (channelID, userID string, ok bool) {
- decoded, err := base64.StdEncoding.DecodeString(cursor)
- if err != nil {
- return "", "", false
- }
-
- parts := strings.Split(string(decoded), "-")
- if len(parts) != 3 {
- return "", "", false
- }
-
- if cursorPrefix(parts[0]) != channelMemberCursorPrefix {
- return "", "", false
- }
-
- return parts[1], parts[2], true
-}
diff --git a/server/channels/api4/resolver_channel_member_test.go b/server/channels/api4/resolver_channel_member_test.go
deleted file mode 100644
index 02198c6584..0000000000
--- a/server/channels/api4/resolver_channel_member_test.go
+++ /dev/null
@@ -1,400 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "encoding/json"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-func TestGraphQLChannelMembers(t *testing.T) {
- os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
- defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
- th := Setup(t).InitBasic()
- defer th.TearDown()
-
- // Adding another team with more channels (public and private)
- 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.Context, th.BasicUser, ch1, false)
- th.App.AddUserToChannel(th.Context, th.BasicUser, ch2, false)
-
- // Creating some msgcount
- th.CreateMessagePostWithClient(th.Client, th.BasicChannel, "basic post")
- th.CreateMessagePostWithClient(th.Client, ch1, "ch1 post")
-
- var q struct {
- ChannelMembers []struct {
- Channel struct {
- ID string `json:"id"`
- CreateAt float64 `json:"createAt"`
- UpdateAt float64 `json:"updateAt"`
- Type model.ChannelType `json:"type"`
- DisplayName string `json:"displayName"`
- Name string `json:"name"`
- Header string `json:"header"`
- Purpose string `json:"purpose"`
- Team struct {
- ID string `json:"id"`
- } `json:"team"`
- } `json:"channel"`
- User struct {
- ID string `json:"id"`
- Username string `json:"username"`
- Email string `json:"email"`
- FirstName string `json:"firstName"`
- LastName string `json:"lastName"`
- NickName string `json:"nickname"`
- } `json:"user"`
- Roles []struct {
- ID string `json:"id"`
- Name string `json:"Name"`
- Permissions []string `json:"permissions"`
- SchemeManaged bool `json:"schemeManaged"`
- BuiltIn bool `json:"builtIn"`
- } `json:"roles"`
- LastViewedAt float64 `json:"lastViewedAt"`
- LastUpdateAt float64 `json:"lastUpdateAt"`
- MsgCount float64 `json:"msgCount"`
- MentionCount float64 `json:"mentionCount"`
- MentionCountRoot float64 `json:"mentionCountRoot"`
- UrgentMentionCount float64 `json:"urgentMentionCount"`
- MsgCountRoot float64 `json:"msgCountRoot"`
- NotifyProps model.StringMap `json:"notifyProps"`
- SchemeGuest bool `json:"schemeGuest"`
- SchemeUser bool `json:"schemeUser"`
- SchemeAdmin bool `json:"schemeAdmin"`
- Cursor string `json:"cursor"`
- } `json:"channelMembers"`
- }
-
- t.Run("all", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "channelMembers",
- Query: `
- query channelMembers {
- channelMembers(userId: "me") {
- channel {
- id
- createAt
- updateAt
- type
- displayName
- name
- header
- team {
- id
- }
- }
- user {
- id
- username
- email
- }
- msgCount
- mentionCount
- mentionCountRoot
- urgentMentionCount
- msgCountRoot
- schemeGuest
- schemeUser
- schemeAdmin
- cursor
- }
- }
- `,
- }
-
- 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.ChannelMembers, 9)
-
- numPrivate := 0
- numPublic := 0
- numOffTopic := 0
- numTownSquare := 0
- for _, ch := range q.ChannelMembers {
- assert.NotEmpty(t, ch.Channel.ID)
- assert.NotEmpty(t, ch.Channel.Name)
- assert.NotEmpty(t, ch.Channel.CreateAt)
- assert.NotEmpty(t, ch.Channel.UpdateAt)
- if ch.Channel.Type == model.ChannelTypeOpen {
- numPublic++
- } else if ch.Channel.Type == model.ChannelTypePrivate {
- numPrivate++
- }
-
- if ch.Channel.DisplayName == "Off-Topic" {
- numOffTopic++
- } else if ch.Channel.DisplayName == "Town Square" {
- numTownSquare++
- }
-
- assert.Equal(t, th.BasicUser.Id, ch.User.ID)
- assert.Equal(t, th.BasicUser.Username, ch.User.Username)
- assert.Equal(t, th.BasicUser.Email, ch.User.Email)
-
- assert.False(t, ch.SchemeGuest)
-
- if ch.Channel.Team.ID == myTeam.Id {
- assert.True(t, ch.SchemeAdmin)
- } else {
- assert.False(t, ch.SchemeAdmin)
- }
- assert.True(t, ch.SchemeUser)
-
- assert.NotEmpty(t, ch.Cursor)
-
- switch ch.Channel.ID {
- case th.BasicChannel.Id:
- assert.Equal(t, float64(2), ch.MsgCount)
- case ch1.Id:
- assert.Equal(t, float64(1), ch.MsgCount)
- }
- }
-
- assert.Equal(t, 2, numPrivate)
- assert.Equal(t, 7, numPublic)
- assert.Equal(t, 2, numOffTopic)
- assert.Equal(t, 2, numTownSquare)
- })
-
- t.Run("user_perms", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "channelMembers",
- Query: `
- query channelMembers($user: String!) {
- channelMembers(userId: $user) {
- channel {
- id
- createAt
- updateAt
- }
- msgCount
- mentionCount
- mentionCountRoot
- urgentMentionCount
- }
- }
- `,
- Variables: map[string]any{
- "user": model.NewId(),
- },
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 1)
- })
-
- t.Run("pagination", func(t *testing.T) {
- query := `query channelMembers($first: Int, $after: String = "") {
- channelMembers(userId: "me", first: $first, after: $after) {
- channel {
- id
- createAt
- updateAt
- type
- displayName
- name
- header
- }
- cursor
- }
- }
- `
- input := graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- },
- }
-
- 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.ChannelMembers, 4)
-
- input = graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- "after": q.ChannelMembers[3].Cursor,
- },
- }
-
- 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.ChannelMembers, 4)
-
- input = graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- "after": q.ChannelMembers[3].Cursor,
- },
- }
-
- 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.ChannelMembers, 1)
- })
-
- t.Run("channel_filter", func(t *testing.T) {
- query := `query channelMembers($channelId: String, $first: Int, $after: String = "") {
- channelMembers(userId: "me", channelId: $channelId, first: $first, after: $after) {
- channel {
- id
- }
- }
- }
- `
- input := graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "channelId": ch1.Id,
- "first": 4,
- },
- }
-
- 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.ChannelMembers, 1)
- assert.Equal(t, q.ChannelMembers[0].Channel.ID, ch1.Id)
-
- input = graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "channelId": model.NewId(),
- "first": 3,
- },
- }
-
- resp, err = th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 1)
- })
-
- t.Run("team_filter", func(t *testing.T) {
- query := `query channelMembers($teamId: String, $excludeTeam: Boolean = false) {
- channelMembers(userId: "me", teamId: $teamId, excludeTeam: $excludeTeam) {
- channel {
- id
- }
- }
- }
- `
- input := graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "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.ChannelMembers, 5)
-
- input = graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "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.ChannelMembers, 4)
- })
-
- t.Run("UpdateAt", func(t *testing.T) {
- query := `query channelMembers($first: Int, $after: String = "", $lastUpdateAt: Float) {
- channelMembers(userId: "me", first: $first, after: $after, lastUpdateAt: $lastUpdateAt) {
- channel {
- id
- }
- lastUpdateAt
- cursor
- }
- }
- `
-
- now := model.GetMillis()
- input := graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- "lastUpdateAt": float64(now),
- },
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0)
- require.NoError(t, json.Unmarshal(resp.Data, &q))
- require.Len(t, q.ChannelMembers, 0)
-
- // Create post to update the lastUpdateAt for the channel member.
- th.CreateMessagePostWithClient(th.Client, th.BasicChannel, "another post")
-
- input = graphQLInput{
- OperationName: "channelMembers",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- "lastUpdateAt": float64(now),
- },
- }
-
- resp, err = th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0)
- require.NoError(t, json.Unmarshal(resp.Data, &q))
- require.Len(t, q.ChannelMembers, 1)
- assert.Equal(t, th.BasicChannel.Id, q.ChannelMembers[0].Channel.ID)
- assert.GreaterOrEqual(t, q.ChannelMembers[0].LastUpdateAt, float64(now))
- })
-}
-
-func TestChannelMemberCursor(t *testing.T) {
- ch := channelMember{
- ChannelMember: model.ChannelMember{ChannelId: "testid", UserId: "userid"},
- }
- cur := ch.Cursor()
-
- chId, userId, ok := parseChannelMemberCursor(*cur)
- require.True(t, ok)
- assert.Equal(t, ch.ChannelId, chId)
- assert.Equal(t, ch.UserId, userId)
-}
diff --git a/server/channels/api4/resolver_channel_test.go b/server/channels/api4/resolver_channel_test.go
deleted file mode 100644
index e36b670c16..0000000000
--- a/server/channels/api4/resolver_channel_test.go
+++ /dev/null
@@ -1,513 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "encoding/json"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-func TestGraphQLChannels(t *testing.T) {
- os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
- defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
- th := Setup(t).InitBasic()
- defer th.TearDown()
-
- // Adding another team with more channels (public and private)
- 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.Context, th.BasicUser, ch1, false)
- th.App.AddUserToChannel(th.Context, th.BasicUser, ch2, false)
- th.CreateDmChannel(th.BasicUser2)
-
- var q struct {
- Channels []struct {
- ID string `json:"id"`
- CreateAt float64 `json:"createAt"`
- UpdateAt float64 `json:"updateAt"`
- Type model.ChannelType `json:"type"`
- DisplayName string `json:"displayName"`
- PrettyDisplayName string `json:"prettyDisplayName"`
- Name string `json:"name"`
- Header string `json:"header"`
- Purpose string `json:"purpose"`
- SchemeId string `json:"schemeId"`
- TotalMsgCountRoot float64 `json:"totalMsgCountRoot"`
- LastRootPostAt float64 `json:"lastRootPostAt"`
- Cursor string `json:"cursor"`
- Props map[string]any `json:"props"`
- Team struct {
- ID string `json:"id"`
- DisplayName string `json:"displayName"`
- } `json:"team"`
- } `json:"channels"`
- }
-
- t.Run("all", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "channels",
- Query: `
- query channels {
- channels(userId: "me") {
- id
- createAt
- updateAt
- type
- displayName
- prettyDisplayName
- name
- header
- purpose
- schemeId
- totalMsgCountRoot
- lastRootPostAt
- cursor
- props
- }
- }
- `,
- }
-
- 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.Channels, 10)
-
- numPrivate := 0
- numPublic := 0
- numOffTopic := 0
- numTownSquare := 0
- for _, ch := range q.Channels {
- assert.NotEmpty(t, ch.ID)
- assert.NotEmpty(t, ch.Name)
- assert.NotEmpty(t, ch.Cursor)
- assert.NotEmpty(t, ch.PrettyDisplayName)
- assert.NotEmpty(t, ch.CreateAt)
- assert.NotEmpty(t, ch.UpdateAt)
- assert.NotNil(t, ch.Props)
- if ch.Type == model.ChannelTypeOpen {
- numPublic++
- } else if ch.Type == model.ChannelTypePrivate {
- numPrivate++
- }
-
- if ch.DisplayName == "Off-Topic" {
- numOffTopic++
- } else if ch.DisplayName == "Town Square" {
- numTownSquare++
- }
- }
-
- assert.Equal(t, 2, numPrivate)
- assert.Equal(t, 7, numPublic)
- assert.Equal(t, 2, numOffTopic)
- assert.Equal(t, 2, numTownSquare)
- })
-
- t.Run("user_perms", func(t *testing.T) {
- query := `query channels($userId: String = "") {
- channels(userId: $userId) {
- id
- createAt
- updateAt
- type
- cursor
- }
- }
- `
- u1 := th.CreateUser()
-
- input := graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "userId": u1.Id,
- },
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 1)
- })
-
- t.Run("pagination", func(t *testing.T) {
- query := `query channels($first: Int, $after: String = "") {
- channels(userId: "me", first: $first, after: $after) {
- id
- createAt
- updateAt
- type
- displayName
- name
- header
- purpose
- schemeId
- cursor
- }
- }
- `
- input := graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- },
- }
-
- 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.Channels, 4)
-
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- "after": q.Channels[3].Cursor,
- },
- }
-
- 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.Channels, 4)
-
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "first": 4,
- "after": q.Channels[3].Cursor,
- },
- }
-
- 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.Channels, 2)
- })
-
- t.Run("team_filter", func(t *testing.T) {
- query := `query channels($teamId: String, $first: Int) {
- channels(userId: "me", teamId: $teamId, first: $first) {
- id
- }
- }
- `
- input := graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "first": 10,
- "teamId": myTeam.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.Channels, 5)
-
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "first": 2,
- "teamId": myTeam.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.Channels, 2)
- })
-
- t.Run("team_data", func(t *testing.T) {
- query := `query channels($teamId: String, $first: Int) {
- channels(userId: "me", teamId: $teamId, first: $first) {
- id
- team {
- id
- displayName
- }
- }
- }
- `
- input := graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "first": 2,
- "teamId": myTeam.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.Channels, 2)
-
- // Iterating because one of them can be a DM channel.
- for _, ch := range q.Channels {
- if ch.Team.ID != "" {
- assert.Equal(t, myTeam.Id, ch.Team.ID)
- assert.Equal(t, myTeam.DisplayName, ch.Team.DisplayName)
- }
- }
- })
-
- t.Run("Delete+Update", func(t *testing.T) {
- query := `query channels($lastDeleteAt: Float = 0,
- $lastUpdateAt: Float = 0,
- $first: Int = 60,
- $includeDeleted: Boolean) {
- channels(userId: "me", lastDeleteAt: $lastDeleteAt, lastUpdateAt: $lastUpdateAt, first: $first, includeDeleted: $includeDeleted) {
- id
- }
- }
- `
- input := graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "includeDeleted": false,
- },
- }
-
- 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.Channels, 10)
-
- now := model.GetMillis()
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "includeDeleted": true,
- "lastUpdateAt": float64(now),
- },
- }
-
- resp, err = th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0) // no errors for no channels found
-
- th.BasicChannel.Purpose = "newpurpose"
- _, _, err = th.Client.UpdateChannel(context.Background(), th.BasicChannel)
- require.NoError(t, err)
-
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "includeDeleted": true,
- "lastUpdateAt": float64(now),
- },
- }
-
- 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.Channels, 1)
-
- _, err = th.Client.DeleteChannel(context.Background(), ch1.Id)
- require.NoError(t, err)
-
- _, err = th.Client.DeleteChannel(context.Background(), ch2.Id)
- require.NoError(t, err)
-
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "includeDeleted": false,
- },
- }
-
- 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.Channels, 8)
-
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "includeDeleted": true,
- "lastDeleteAt": float64(model.GetMillis()),
- },
- }
-
- 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.Channels, 8)
-
- input = graphQLInput{
- OperationName: "channels",
- Query: query,
- Variables: map[string]any{
- "includeDeleted": true,
- "lastDeleteAt": float64(model.GetMillis()),
- "first": 5,
- },
- }
-
- 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.Channels, 5)
- })
-}
-
-func TestGetPrettyDNForUsers(t *testing.T) {
- os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
- defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
- t.Run("nickname_full_name", func(t *testing.T) {
- users := []*model.User{
- {
- Id: "user1",
- Nickname: "nick1",
- Username: "user1",
- FirstName: "first1",
- LastName: "last1",
- },
- {
- Id: "user2",
- Nickname: "nick2",
- Username: "user2",
- FirstName: "first2",
- LastName: "last2",
- },
- }
- assert.Equal(t, "nick2", getPrettyDNForUsers("nickname_full_name", users, "user1", map[string]string{}))
-
- users = []*model.User{
- {
- Id: "user1",
- Username: "user1",
- FirstName: "first1",
- LastName: "last1",
- },
- {
- Id: "user2",
- Username: "user2",
- FirstName: "first2",
- LastName: "last2",
- },
- }
- assert.Equal(t, "first2 last2", getPrettyDNForUsers("nickname_full_name", users, "user1", map[string]string{}))
- })
-
- t.Run("full_name", func(t *testing.T) {
- users := []*model.User{
- {
- Id: "user1",
- Nickname: "nick1",
- Username: "user1",
- FirstName: "first1",
- LastName: "last1",
- },
- {
- Id: "user2",
- Nickname: "nick2",
- Username: "user2",
- FirstName: "first2",
- LastName: "last2",
- },
- }
- assert.Equal(t, "first2 last2", getPrettyDNForUsers("full_name", users, "user1", map[string]string{}))
-
- users = []*model.User{
- {
- Id: "user1",
- Username: "user1",
- },
- {
- Id: "user2",
- Username: "user2",
- },
- }
- assert.Equal(t, "user2", getPrettyDNForUsers("full_name", users, "user1", map[string]string{}))
- })
-
- t.Run("username", func(t *testing.T) {
- users := []*model.User{
- {
- Id: "user1",
- Nickname: "nick1",
- Username: "user1",
- FirstName: "first1",
- LastName: "last1",
- },
- {
- Id: "user2",
- Nickname: "nick2",
- Username: "user2",
- FirstName: "first2",
- LastName: "last2",
- },
- }
- assert.Equal(t, "user2", getPrettyDNForUsers("username", users, "user1", map[string]string{}))
- })
-
- t.Run("cache", func(t *testing.T) {
- users := []*model.User{
- {
- Id: "user1",
- Nickname: "nick1",
- Username: "user1",
- FirstName: "first1",
- LastName: "last1",
- },
- {
- Id: "user2",
- Nickname: "nick2",
- Username: "user2",
- FirstName: "first2",
- LastName: "last2",
- },
- }
-
- cache := map[string]string{}
- assert.Equal(t, "first2 last2", getPrettyDNForUsers("full_name", users, "user1", cache))
- cache["user2"] = "teststring!!"
- assert.Equal(t, "teststring!!", getPrettyDNForUsers("full_name", users, "user1", cache))
- })
-}
-
-func TestChannelCursor(t *testing.T) {
- ch := channel{
- Channel: model.Channel{Id: "testid"},
- }
- cur := ch.Cursor()
-
- id, ok := parseChannelCursor(*cur)
- require.True(t, ok)
- assert.Equal(t, ch.Id, id)
-}
diff --git a/server/channels/api4/resolver_sidebar_categories_test.go b/server/channels/api4/resolver_sidebar_categories_test.go
deleted file mode 100644
index 65d69905f6..0000000000
--- a/server/channels/api4/resolver_sidebar_categories_test.go
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "encoding/json"
- "os"
- "sort"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-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"`
- TeamID string `json:"teamId"`
- SortOrder int64 `json:"sortOrder"`
- } `json:"sidebarCategories"`
- }
-
- 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
- sortOrder
- }
- }
- `,
- Variables: map[string]any{
- "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(context.Background(), 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)
- assert.Equal(t, categories.Categories[i].SortOrder, q.SidebarCategories[i].SortOrder)
- }
-
- 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
- sortOrder
- }
- }
- `,
- Variables: map[string]any{
- "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.Context, th.BasicUser, ch1, false)
- th.App.AddUserToChannel(th.Context, 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
- sortOrder
- }
- }
- `,
- Variables: map[string]any{
- "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/server/channels/api4/resolver_team.go b/server/channels/api4/resolver_team.go
deleted file mode 100644
index 6fedd8174a..0000000000
--- a/server/channels/api4/resolver_team.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "fmt"
-
- "github.com/graph-gophers/dataloader/v6"
-
- "github.com/mattermost/mattermost/server/public/model"
- "github.com/mattermost/mattermost/server/v8/channels/web"
-)
-
-func getGraphQLTeam(ctx context.Context, id string) (*model.Team, error) {
- loader, err := getTeamsLoader(ctx)
- if err != nil {
- return nil, err
- }
-
- thunk := loader.Load(ctx, dataloader.StringKey(id))
- result, err := thunk()
- if err != nil {
- return nil, err
- }
- team := result.(*model.Team)
- return team, nil
-}
-
-func graphQLTeamsLoader(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
- stringKeys := keys.Keys()
- result := make([]*dataloader.Result, len(stringKeys))
-
- c, err := getCtx(ctx)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- teams, err := getGraphQLTeams(c, stringKeys)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- for i, ch := range teams {
- result[i] = &dataloader.Result{Data: ch}
- }
- return result
-}
-
-func getGraphQLTeams(c *web.Context, teamIDs []string) ([]*model.Team, error) {
- teams, appErr := c.App.GetTeams(teamIDs)
- if appErr != nil {
- return nil, appErr
- }
-
- if len(teams) != len(teamIDs) {
- return nil, fmt.Errorf("all teams were not found. Requested %d; Found %d", len(teamIDs), len(teams))
- }
-
- var teamsToCheck []string
- for _, team := range teams {
- if !team.AllowOpenInvite || team.Type != model.TeamOpen {
- teamsToCheck = append(teamsToCheck, team.Id)
- }
- }
-
- if !c.App.SessionHasPermissionToTeams(c.AppContext, *c.AppContext.Session(), teamsToCheck, model.PermissionViewTeam) {
- c.SetPermissionError(model.PermissionViewTeam)
- return nil, c.Err
- }
-
- for i, team := range teams {
- teams[i] = c.App.SanitizeTeam(*c.AppContext.Session(), team)
- }
-
- // The teams need to be in the exact same order as the input slice.
- tmp := make(map[string]*model.Team, len(teams))
- for _, ch := range teams {
- tmp[ch.Id] = ch
- }
-
- // We reuse the same slice and just rewrite the teams.
- for i, id := range teamIDs {
- teams[i] = tmp[id]
- }
-
- return teams, nil
-}
diff --git a/server/channels/api4/resolver_team_member.go b/server/channels/api4/resolver_team_member.go
deleted file mode 100644
index 5facac57d4..0000000000
--- a/server/channels/api4/resolver_team_member.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "strings"
-
- "github.com/graph-gophers/dataloader/v6"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-// teamMember is an internal graphQL wrapper struct to add resolver methods.
-type teamMember struct {
- model.TeamMember
-}
-
-// match with api4.getTeam
-func (tm *teamMember) Team(ctx context.Context) (*model.Team, error) {
- return getGraphQLTeam(ctx, tm.TeamId)
-}
-
-// match with api4.getUser
-func (tm *teamMember) User(ctx context.Context) (*user, error) {
- return getGraphQLUser(ctx, tm.UserId)
-}
-
-// match with api4.getRolesByNames
-func (tm *teamMember) Roles_(ctx context.Context) ([]*model.Role, error) {
- loader, err := getRolesLoader(ctx)
- if err != nil {
- return nil, err
- }
-
- thunk := loader.LoadMany(ctx, dataloader.NewKeysFromStrings(strings.Fields(tm.Roles)))
- results, errs := thunk()
- // All errors are the same. We just return the first one.
- if len(errs) > 0 && errs[0] != nil {
- return nil, err
- }
-
- roles := make([]*model.Role, len(results))
- for i, res := range results {
- roles[i] = res.(*model.Role)
- }
-
- return roles, nil
-}
diff --git a/server/channels/api4/resolver_team_member_test.go b/server/channels/api4/resolver_team_member_test.go
deleted file mode 100644
index e371b2e1ce..0000000000
--- a/server/channels/api4/resolver_team_member_test.go
+++ /dev/null
@@ -1,413 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "encoding/json"
- "os"
- "sort"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-func TestGraphQLTeamMembers(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 {
- TeamMembers []struct {
- User struct {
- ID string `json:"id"`
- Username string `json:"username"`
- Email string `json:"email"`
- FirstName string `json:"firstName"`
- LastName string `json:"lastName"`
- NickName string `json:"nickname"`
- } `json:"user"`
- Team struct {
- ID string `json:"id"`
- DisplayName string `json:"displayName"`
- Name string `json:"name"`
- CreateAt float64 `json:"createAt"`
- DeleteAt float64 `json:"deleteAt"`
- SchemeId *string `json:"schemeId"`
- PolicyId *string `json:"policyId"`
- CloudLimitsArchived bool `json:"cloudLimitsArchived"`
- } `json:"team"`
- Roles []struct {
- ID string `json:"id"`
- Name string `json:"Name"`
- Permissions []string `json:"permissions"`
- 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"`
- } `json:"teamMembers"`
- }
-
- t.Run("User", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "teamMembers",
- Query: `
- query teamMembers($userId: String = "", $teamId: String = "") {
- teamMembers(userId: $userId, teamId: $teamId) {
- team {
- id
- displayName
- }
- user {
- id
- username
- email
- firstName
- lastName
- }
- roles {
- id
- name
- }
- schemeGuest
- schemeUser
- schemeAdmin
- }
- }
- `,
- Variables: map[string]any{
- "userId": "me",
- },
- }
-
- 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)
-
- tm := q.TeamMembers[0]
- assert.Equal(t, th.BasicTeam.Id, tm.Team.ID)
- assert.Equal(t, th.BasicTeam.DisplayName, tm.Team.DisplayName)
-
- assert.Equal(t, th.BasicUser.Id, tm.User.ID)
- assert.Equal(t, th.BasicUser.Username, tm.User.Username)
- assert.Equal(t, th.BasicUser.Email, tm.User.Email)
- assert.Equal(t, th.BasicUser.FirstName, tm.User.FirstName)
- assert.Equal(t, th.BasicUser.LastName, tm.User.LastName)
-
- require.Len(t, tm.Roles, 1)
- assert.NotEmpty(t, tm.Roles[0].ID)
- assert.Equal(t, "team_user", tm.Roles[0].Name)
- assert.False(t, tm.SchemeGuest)
- assert.True(t, tm.SchemeUser)
- assert.False(t, tm.SchemeAdmin)
- })
-
- t.Run("User+Team", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "teamMembers",
- Query: `
- query teamMembers($userId: String = "", $teamId: String = "") {
- teamMembers(userId: $userId, teamId: $teamId) {
- team {
- id
- displayName
- name
- createAt
- deleteAt
- schemeId
- policyId
- cloudLimitsArchived
- }
- user {
- id
- username
- email
- firstName
- lastName
- }
- roles {
- id
- name
- }
- }
- }
- `,
- Variables: map[string]any{
- "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)
-
- tm := q.TeamMembers[0]
- assert.Equal(t, th.BasicTeam.Id, tm.Team.ID)
- assert.Equal(t, th.BasicTeam.DisplayName, tm.Team.DisplayName)
- assert.Equal(t, th.BasicTeam.Name, tm.Team.Name)
- assert.Equal(t, th.BasicTeam.CreateAt_(), tm.Team.CreateAt)
- assert.Equal(t, th.BasicTeam.DeleteAt_(), tm.Team.DeleteAt)
- assert.Equal(t, th.BasicTeam.SchemeId, tm.Team.SchemeId)
- assert.Equal(t, th.BasicTeam.PolicyID, tm.Team.PolicyId)
- assert.Equal(t, th.BasicTeam.CloudLimitsArchived, tm.Team.CloudLimitsArchived)
-
- assert.Equal(t, th.BasicUser.Id, tm.User.ID)
- assert.Equal(t, th.BasicUser.Username, tm.User.Username)
- assert.Equal(t, th.BasicUser.Email, tm.User.Email)
- assert.Equal(t, th.BasicUser.FirstName, tm.User.FirstName)
- assert.Equal(t, th.BasicUser.LastName, tm.User.LastName)
-
- require.Len(t, tm.Roles, 1)
- assert.NotEmpty(t, tm.Roles[0].ID)
- assert.Equal(t, "team_user", tm.Roles[0].Name)
- })
-
- t.Run("NewTeam", func(t *testing.T) {
- // Adding another team with more channels (public and private)
- 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.Context, th.BasicUser, ch1, false)
- th.App.AddUserToChannel(th.Context, th.BasicUser, ch2, false)
-
- input := graphQLInput{
- OperationName: "teamMembers",
- Query: `
- query teamMembers($userId: String = "", $teamId: String = "") {
- teamMembers(userId: $userId, teamId: $teamId) {
- team {
- id
- displayName
- }
- roles {
- id
- name
- }
- }
- }
- `,
- Variables: map[string]any{
- "userId": "me",
- },
- }
-
- 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, 2)
-
- sort.Slice(q.TeamMembers, func(i, j int) bool {
- return q.TeamMembers[i].Team.ID < q.TeamMembers[j].Team.ID
- })
-
- expectedTeams := []*model.Team{th.BasicTeam, myTeam}
- sort.Slice(expectedTeams, func(i, j int) bool {
- return expectedTeams[i].Id < expectedTeams[j].Id
- })
-
- for i := range q.TeamMembers {
- tm := q.TeamMembers[i]
-
- if tm.Team.ID == myTeam.Id {
- require.Len(t, tm.Roles, 2)
- sort.Slice(tm.Roles, func(i, j int) bool {
- return tm.Roles[i].Name < tm.Roles[j].Name
- })
- assert.Equal(t, "team_admin", tm.Roles[0].Name)
- assert.Equal(t, "team_user", tm.Roles[1].Name)
- } else {
- require.Len(t, tm.Roles, 1)
- assert.NotEmpty(t, tm.Roles[0].ID)
- assert.Equal(t, "team_user", tm.Roles[0].Name)
- }
-
- expectedTeams[i].Id = tm.Team.ID
- 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]any{
- "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]any{
- "userId": "me",
- },
- }
-
- // Removing from a team and ensuring we get the right response.
- th.UnlinkUserFromTeam(th.BasicUser, myTeam)
- 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)
- })
-}
-
-func TestGraphQLTeamMembersAsGuest(t *testing.T) {
- os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
- defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
-
- th := Setup(t)
-
- id := model.NewId()
- team := &model.Team{
- DisplayName: "dn_" + id,
- Name: GenerateTestTeamName(),
- Email: th.GenerateTestEmail(),
- Type: model.TeamOpen,
- AllowOpenInvite: true,
- }
-
- var err error
- team, _, err = th.Client.CreateTeam(context.Background(), team)
- require.NoError(t, err)
- th.BasicTeam = team
-
- th.BasicChannel = th.CreatePublicChannel()
- th.LinkUserToTeam(th.BasicUser, th.BasicTeam)
- th.App.AddUserToChannel(th.Context, th.BasicUser, th.BasicChannel, false)
- th.LoginBasic()
-
- defer th.TearDown()
-
- require.Nil(t, th.App.DemoteUserToGuest(th.Context, th.BasicUser))
-
- var q struct {
- TeamMembers []struct {
- User struct {
- ID string `json:"id"`
- Username string `json:"username"`
- Email string `json:"email"`
- FirstName string `json:"firstName"`
- LastName string `json:"lastName"`
- NickName string `json:"nickname"`
- } `json:"user"`
- Team struct {
- ID string `json:"id"`
- DisplayName string `json:"displayName"`
- Name string `json:"name"`
- CreateAt float64 `json:"createAt"`
- DeleteAt float64 `json:"deleteAt"`
- SchemeId *string `json:"schemeId"`
- PolicyId *string `json:"policyId"`
- CloudLimitsArchived bool `json:"cloudLimitsArchived"`
- } `json:"team"`
- Roles []struct {
- ID string `json:"id"`
- Name string `json:"Name"`
- Permissions []string `json:"permissions"`
- 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"`
- } `json:"teamMembers"`
- }
-
- t.Run("User", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "teamMembers",
- Query: `
- query teamMembers($userId: String = "", $teamId: String = "") {
- teamMembers(userId: $userId, teamId: $teamId) {
- team {
- id
- displayName
- }
- user {
- id
- username
- email
- firstName
- lastName
- }
- roles {
- id
- name
- }
- schemeGuest
- schemeUser
- schemeAdmin
- }
- }
- `,
- Variables: map[string]any{
- "userId": "me",
- },
- }
-
- 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)
-
- tm := q.TeamMembers[0]
- assert.Equal(t, th.BasicTeam.Id, tm.Team.ID)
- assert.Equal(t, th.BasicTeam.DisplayName, tm.Team.DisplayName)
-
- assert.Equal(t, th.BasicUser.Id, tm.User.ID)
- assert.Equal(t, th.BasicUser.Username, tm.User.Username)
- assert.Equal(t, th.BasicUser.Email, tm.User.Email)
- assert.Equal(t, th.BasicUser.FirstName, tm.User.FirstName)
- assert.Equal(t, th.BasicUser.LastName, tm.User.LastName)
-
- require.Len(t, tm.Roles, 1)
- assert.NotEmpty(t, tm.Roles[0].ID)
- assert.Equal(t, "team_guest", tm.Roles[0].Name)
- assert.True(t, tm.SchemeGuest)
- assert.False(t, tm.SchemeUser)
- assert.False(t, tm.SchemeAdmin)
- })
-}
diff --git a/server/channels/api4/resolver_test.go b/server/channels/api4/resolver_test.go
deleted file mode 100644
index 13b6111f39..0000000000
--- a/server/channels/api4/resolver_test.go
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "encoding/json"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-func TestGraphQLConfig(t *testing.T) {
- os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
- defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
-
- th := Setup(t)
- th.LoginBasicWithGraphQL()
- defer th.TearDown()
-
- var q struct {
- Config map[string]string `json:"config"`
- }
-
- input := graphQLInput{
- OperationName: "config",
- Query: `
- query config {
- config
- }
- `,
- }
-
- cfg, _, err := th.Client.GetOldClientConfig(context.Background(), "")
- require.NoError(t, err)
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0)
- require.NoError(t, json.Unmarshal(resp.Data, &q))
- assert.Equal(t, cfg, q.Config)
-}
-
-func TestGraphQLLicense(t *testing.T) {
- os.Setenv("MM_FEATUREFLAGS_GRAPHQL", "true")
- defer os.Unsetenv("MM_FEATUREFLAGS_GRAPHQL")
-
- th := Setup(t)
- th.LoginBasicWithGraphQL()
- defer th.TearDown()
-
- var q struct {
- License map[string]string `json:"license"`
- }
-
- input := graphQLInput{
- OperationName: "license",
- Query: `
- query license {
- license
- }
- `,
- }
-
- cfg, _, err := th.Client.GetOldClientLicense(context.Background(), "")
- require.NoError(t, err)
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0)
- require.NoError(t, json.Unmarshal(resp.Data, &q))
- assert.Equal(t, cfg, q.License)
-}
-
-func TestGraphQLChannelsLeft(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 {
- ChannelsLeft []string `json:"channelsLeft"`
- }
-
- t.Run("NotLeft", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "channelsLeft",
- Query: `
- query channelsLeft($userId: String = "me", $since: Float = 0.0) {
- channelsLeft(userId: $userId, since: $since)
- }
- `,
- }
-
- 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.ChannelsLeft, 0)
- })
-
- t.Run("Left", func(t *testing.T) {
- _, err := th.Client.RemoveUserFromChannel(context.Background(), th.BasicChannel.Id, th.BasicUser.Id)
- require.NoError(t, err)
-
- input := graphQLInput{
- OperationName: "channelsLeft",
- Query: `
- query channelsLeft($userId: String = "me", $since: Float = 0.0) {
- channelsLeft(userId: $userId, since: $since)
- }
- `,
- }
-
- 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.ChannelsLeft, 1)
- })
-
- t.Run("LeftAfterTime", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "channelsLeft",
- Query: `
- query channelsLeft($userId: String = "me", $since: Float = 0.0) {
- channelsLeft(userId: $userId, since: $since)
- }
- `,
- Variables: map[string]any{
- "since": model.GetMillis(),
- },
- }
-
- 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.ChannelsLeft, 0)
- })
-}
-
-func TestGraphQLRolesLoader(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 {
- User struct {
- ID string `json:"id"`
- Roles []struct {
- ID string `json:"id"`
- Name string `json:"Name"`
- } `json:"roles"`
- } `json:"user"`
- ChannelMembers []struct {
- MsgCount float64 `json:"msgCount"`
- Roles []struct {
- ID string `json:"id"`
- Name string `json:"Name"`
- } `json:"roles"`
- } `json:"channelMembers"`
- TeamMembers []struct {
- SchemeUser bool `json:"schemeUser"`
- Roles []struct {
- ID string `json:"id"`
- Name string `json:"Name"`
- } `json:"roles"`
- }
- }
-
- input := graphQLInput{
- OperationName: "channelMembers",
- Query: `
- query channelMembers {
- user(id: "me") {
- id
- username
- roles {
- id
- name
- }
- }
- channelMembers(userId: "me") {
- msgCount
- roles {
- id
- name
- }
- }
- teamMembers(userId: "me") {
- schemeUser
- roles {
- id
- name
- }
- }
- }
- `,
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0)
- require.NoError(t, json.Unmarshal(resp.Data, &q))
-
- require.Len(t, q.User.Roles, 1)
- assert.Equal(t, "system_user", q.User.Roles[0].Name)
-
- require.Len(t, q.ChannelMembers, 5)
- for _, cm := range q.ChannelMembers {
- require.Len(t, cm.Roles, 1)
- assert.Equal(t, "channel_user", cm.Roles[0].Name)
- }
-
- require.Len(t, q.TeamMembers, 1)
- for _, tm := range q.TeamMembers {
- require.Len(t, tm.Roles, 1)
- assert.Equal(t, "team_user", tm.Roles[0].Name)
- }
-}
diff --git a/server/channels/api4/resolver_user.go b/server/channels/api4/resolver_user.go
deleted file mode 100644
index dcacd6f61c..0000000000
--- a/server/channels/api4/resolver_user.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "net/http"
-
- "github.com/graph-gophers/dataloader/v6"
-
- "github.com/mattermost/mattermost/server/public/model"
- "github.com/mattermost/mattermost/server/v8/channels/web"
-)
-
-// user is an internal graphQL wrapper struct to add resolver methods.
-type user struct {
- model.User
-}
-
-// match with api4.getUser
-func getGraphQLUser(ctx context.Context, id string) (*user, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if id == model.Me {
- id = c.AppContext.Session().UserId
- }
-
- if !model.IsValidId(id) {
- return nil, web.NewInvalidParamError("user_id")
- }
-
- loader, err := getUsersLoader(ctx)
- if err != nil {
- return nil, err
- }
-
- thunk := loader.Load(ctx, dataloader.StringKey(id))
- result, err := thunk()
- if err != nil {
- return nil, err
- }
- usr := result.(*model.User)
-
- if c.IsSystemAdmin() || c.AppContext.Session().UserId == usr.Id {
- userTermsOfService, appErr := c.App.GetUserTermsOfService(usr.Id)
- if appErr != nil && appErr.StatusCode != http.StatusNotFound {
- return nil, appErr
- }
-
- if userTermsOfService != nil {
- usr.TermsOfServiceId = userTermsOfService.TermsOfServiceId
- usr.TermsOfServiceCreateAt = userTermsOfService.CreateAt
- }
- }
-
- c.App.Srv().Platform().UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
-
- return &user{*usr}, nil
-}
-
-// match with api4.getRolesByNames
-func (u *user) Roles(ctx context.Context) ([]*model.Role, error) {
- roleNames := u.GetRoles()
- if len(roleNames) == 0 {
- return nil, nil
- }
-
- loader, err := getRolesLoader(ctx)
- if err != nil {
- return nil, err
- }
-
- thunk := loader.LoadMany(ctx, dataloader.NewKeysFromStrings(roleNames))
- results, errs := thunk()
- // All errors are the same. We just return the first one.
- if len(errs) > 0 && errs[0] != nil {
- return nil, err
- }
-
- roles := make([]*model.Role, len(results))
- for i, res := range results {
- roles[i] = res.(*model.Role)
- }
-
- return roles, nil
-}
-
-// match with api4.getPreferences
-func (u *user) Preferences(ctx context.Context) ([]model.Preference, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), u.Id) {
- c.SetPermissionError(model.PermissionEditOtherUsers)
- return nil, c.Err
- }
-
- preferences, appErr := c.App.GetPreferencesForUser(u.Id)
- if appErr != nil {
- return nil, appErr
- }
- return preferences, nil
-}
-
-// match with api4.getUserStatus
-func (u *user) Status(ctx context.Context) (*model.Status, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- statuses, appErr := c.App.GetUserStatusesByIds([]string{u.Id})
- if appErr != nil {
- return nil, appErr
- }
-
- if len(statuses) == 0 {
- return nil, model.NewAppError("UserStatus", "api.status.user_not_found.app_error", nil, "", http.StatusNotFound)
- }
-
- return statuses[0], nil
-}
-
-// match with api4.getSessions
-func (u *user) Sessions(ctx context.Context) ([]*model.Session, error) {
- c, err := getCtx(ctx)
- if err != nil {
- return nil, err
- }
-
- if !c.App.SessionHasPermissionToUser(*c.AppContext.Session(), u.Id) {
- c.SetPermissionError(model.PermissionEditOtherUsers)
- return nil, c.Err
- }
-
- sessions, appErr := c.App.GetSessions(c.AppContext, u.Id)
- if appErr != nil {
- return nil, appErr
- }
-
- for _, session := range sessions {
- session.Sanitize()
- }
-
- return sessions, nil
-}
-
-func graphQLUsersLoader(ctx context.Context, keys dataloader.Keys) []*dataloader.Result {
- stringKeys := keys.Keys()
- result := make([]*dataloader.Result, len(stringKeys))
-
- c, err := getCtx(ctx)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- users, err := getGraphQLUsers(c, stringKeys)
- if err != nil {
- for i := range result {
- result[i] = &dataloader.Result{Error: err}
- }
- return result
- }
-
- for i, user := range users {
- result[i] = &dataloader.Result{Data: user}
- }
-
- return result
-}
-
-func getGraphQLUsers(c *web.Context, userIDs []string) ([]*model.User, error) {
- // Usually this will be called only for one user
- // and cached for the rest of the query. So it's not an issue
- // to run this in a loop.
- for _, id := range userIDs {
- canSee, appErr := c.App.UserCanSeeOtherUser(c.AppContext, c.AppContext.Session().UserId, id)
- if appErr != nil || !canSee {
- c.SetPermissionError(model.PermissionViewMembers)
- return nil, c.Err
- }
- }
-
- users, appErr := c.App.GetUsers(userIDs)
- if appErr != nil {
- return nil, appErr
- }
-
- // Same as earlier, we want to pre-compute this only once
- // because otherwise the resolvers run in multiple goroutines
- // and *User.Sanitize causes a race, and we want to avoid
- // deep-copying every user in all goroutines.
- for _, user := range users {
- if c.AppContext.Session().UserId == user.Id {
- user.Sanitize(map[string]bool{})
- } else {
- c.App.SanitizeProfile(user, c.IsSystemAdmin())
- }
- }
-
- // The users need to be in the exact same order as the input slice.
- tmp := make(map[string]*model.User)
- for _, u := range users {
- tmp[u.Id] = u
- }
-
- // We reuse the same slice and just rewrite the roles.
- for i, uID := range userIDs {
- users[i] = tmp[uID]
- }
-
- return users, nil
-}
diff --git a/server/channels/api4/resolver_user_test.go b/server/channels/api4/resolver_user_test.go
deleted file mode 100644
index 267bf5fb35..0000000000
--- a/server/channels/api4/resolver_user_test.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package api4
-
-import (
- "context"
- "encoding/json"
- "os"
- "sort"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "github.com/mattermost/mattermost/server/public/model"
-)
-
-func TestGraphQLUser(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 {
- User struct {
- ID string `json:"id"`
- Username string `json:"username"`
- Email string `json:"email"`
- FirstName string `json:"firstName"`
- LastName string `json:"lastName"`
- NickName string `json:"nickname"`
- IsBot bool `json:"isBot"`
- IsSystemAdmin bool `json:"isSystemAdmin"`
- CreateAt float64 `json:"createAt"`
- DeleteAt float64 `json:"deleteAt"`
- UpdateAt float64 `json:"updateAt"`
- AuthData *string `json:"authData"`
- EmailVerified bool `json:"emailVerified"`
- CustomStatus struct {
- Emoji string `json:"emoji"`
- Text string `json:"text"`
- Duration string `json:"duration"`
- ExpiresAt time.Time `json:"expiresAt"`
- } `json:"customStatus"`
- Timezone model.StringMap `json:"timezone"`
- Props model.StringMap `json:"props"`
- NotifyProps model.StringMap `json:"notifyProps"`
- Position string `json:"position"`
- Roles []struct {
- ID string `json:"id"`
- Name string `json:"Name"`
- Permissions []string `json:"permissions"`
- SchemeManaged bool `json:"schemeManaged"`
- BuiltIn bool `json:"builtIn"`
- CreateAt float64 `json:"createAt"`
- DeleteAt float64 `json:"deleteAt"`
- UpdateAt float64 `json:"updateAt"`
- } `json:"roles"`
- Preferences []struct {
- UserID string `json:"userId"`
- Category string `json:"category"`
- Name string `json:"name"`
- Value string `json:"value"`
- } `json:"preferences"`
- Sessions []struct {
- ID string `json:"id"`
- CreateAt float64 `json:"createAt"`
- LastActivityAt float64 `json:"lastActivityAt"`
- DeviceId string `json:"deviceId"`
- Roles string `json:"roles"`
- } `json:"sessions"`
- } `json:"user"`
- }
-
- t.Run("Basic", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "user",
- Query: `
- query user($id: String = "me") {
- user(id: $id) {
- id
- username
- email
- createAt
- updateAt
- deleteAt
- firstName
- lastName
- emailVerified
- isBot
- isGuest
- isSystemAdmin
- timezone
- props
- notifyProps
- roles {
- id
- name
- createAt
- updateAt
- deleteAt
- }
- preferences {
- name
- value
- }
- sessions {
- id
- createAt
- lastActivityAt
- roles
- }
- }
- }
- `,
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0)
- require.NoError(t, json.Unmarshal(resp.Data, &q))
- assert.Equal(t, th.BasicUser.Id, q.User.ID)
- assert.Equal(t, th.BasicUser.Username, q.User.Username)
- assert.Equal(t, th.BasicUser.Email, q.User.Email)
- assert.Equal(t, th.BasicUser.FirstName, q.User.FirstName)
- assert.Equal(t, th.BasicUser.IsBot, q.User.IsBot)
- assert.Equal(t, float64(th.BasicUser.CreateAt), q.User.CreateAt)
- assert.Equal(t, float64(th.BasicUser.DeleteAt), q.User.DeleteAt)
- assert.NotZero(t, q.User.UpdateAt)
- assert.Equal(t, th.BasicUser.IsSystemAdmin(), q.User.IsSystemAdmin)
- assert.Equal(t, th.BasicUser.Timezone, q.User.Timezone)
- assert.Equal(t, th.BasicUser.Props, q.User.Props)
- assert.Equal(t, th.BasicUser.NotifyProps, q.User.NotifyProps)
-
- roles, _, err := th.Client.GetRolesByNames(context.Background(), th.BasicUser.GetRoles())
- require.NoError(t, err)
-
- assert.Len(t, q.User.Roles, 1)
- assert.Len(t, roles, 1)
- assert.Equal(t, roles[0].Id, q.User.Roles[0].ID)
- assert.Equal(t, roles[0].Name, q.User.Roles[0].Name)
- assert.Equal(t, float64(roles[0].CreateAt), q.User.Roles[0].CreateAt)
- assert.Equal(t, float64(roles[0].UpdateAt), q.User.Roles[0].UpdateAt)
- assert.Equal(t, float64(roles[0].DeleteAt), q.User.Roles[0].DeleteAt)
-
- prefs, _, err := th.Client.GetPreferences(context.Background(), th.BasicUser.Id)
- require.NoError(t, err)
-
- sort.Slice(prefs, func(i, j int) bool {
- return prefs[i].Name < prefs[j].Name
- })
-
- sort.Slice(q.User.Preferences, func(i, j int) bool {
- return q.User.Preferences[i].Name < q.User.Preferences[j].Name
- })
-
- for i := range prefs {
- assert.Equal(t, q.User.Preferences[i].Name, prefs[i].Name)
- assert.Equal(t, q.User.Preferences[i].Value, prefs[i].Value)
- }
-
- assert.Len(t, q.User.Sessions, 2)
- now := float64(model.GetMillis())
- for _, session := range q.User.Sessions {
- assert.NotEmpty(t, session.ID)
- assert.Less(t, session.CreateAt, now)
- assert.Less(t, session.LastActivityAt, now)
- assert.Equal(t, model.SystemUserRoleId, session.Roles)
- }
- })
-
- t.Run("Update", func(t *testing.T) {
- th.BasicUser.Props = map[string]string{"testpropkey": "testpropvalue"}
- th.App.UpdateUser(th.Context, th.BasicUser, false)
-
- input := graphQLInput{
- OperationName: "user",
- Query: `
- query user($id: String = "me") {
- user(id: $id) {
- id
- props
- }
- }
- `,
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 0)
- require.NoError(t, json.Unmarshal(resp.Data, &q))
-
- assert.Equal(t, th.BasicUser.Props, q.User.Props)
- })
-
- t.Run("DifferentUser", func(t *testing.T) {
- input := graphQLInput{
- OperationName: "user",
- Query: `
- query user($id: String = "me") {
- user(id: $id) {
- id
- props
- }
- }
- `,
- Variables: map[string]any{
- "id": th.BasicUser2.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.Equal(t, q.User.ID, th.BasicUser2.Id)
- })
-
- t.Run("BadUser", func(t *testing.T) {
- id := model.NewId()
- input := graphQLInput{
- OperationName: "user",
- Query: `
- query user($id: String = "me") {
- user(id: $id) {
- id
- props
- }
- }
- `,
- Variables: map[string]any{
- "id": id,
- },
- }
-
- resp, err := th.MakeGraphQLRequest(&input)
- require.NoError(t, err)
- require.Len(t, resp.Errors, 1)
- })
-}
diff --git a/server/channels/api4/schema.graphqls b/server/channels/api4/schema.graphqls
deleted file mode 100644
index e277832f04..0000000000
--- a/server/channels/api4/schema.graphqls
+++ /dev/null
@@ -1,222 +0,0 @@
-schema {
- query: Query
-}
-
-type Query {
- user(id: String!): User
- config(): StringMap!
- license(): StringMap!
- teamMembers(userId: String!,
- teamId: String = "",
- excludeTeam: Boolean = false): [TeamMember]!
- channels(userId: String!,
- teamId: String = "",
- includeDeleted: Boolean = false,
- lastDeleteAt: Float = 0,
- lastUpdateAt: Float = 0,
- first: Int = 60,
- after: String = ""): [Channel]!
- channelsLeft(userId: String!,
- since: Float!): [String!]!
- channelMembers(userId: String!,
- channelId: String = "",
- teamId: String = "",
- excludeTeam: Boolean = false,
- first: Int = 60,
- after: String = "",
- lastUpdateAt: Float = 0): [ChannelMember]!
- sidebarCategories(userId: String!,
- teamId: String!,
- excludeTeam: Boolean = false): [SidebarCategory]!
-}
-
-scalar ChannelType
-
-scalar SidebarCategoryType
-
-scalar SidebarCategorySorting
-
-scalar StringMap
-
-scalar StringInterface
-
-scalar Time
-
-type Channel {
- id : String!
- createAt : Float!
- updateAt : Float!
- deleteAt : Float!
- type : ChannelType!
- displayName: String!
- prettyDisplayName: String!
- name: String!
- header: String!
- purpose: String!
- creatorId: String!
- schemeId: String
- team: Team
- groupConstrained: Boolean
- shared: Boolean
- lastPostAt: Float!
- totalMsgCount: Float!
- totalMsgCountRoot: Float!
- lastRootPostAt: Float!
- extraUpdateAt: Float!
- props: StringInterface!
- policyId: String
- cursor: String
-}
-
-type ChannelMember {
- channel : Channel
- user : User
- roles : [Role]!
- lastViewedAt : Float!
- msgCount : Float!
- mentionCount : Float!
- urgentMentionCount: Float!
- mentionCountRoot : Float!
- msgCountRoot : Float!
- notifyProps : StringMap!
- lastUpdateAt : Float!
- schemeGuest : Boolean!
- schemeUser : Boolean!
- schemeAdmin : Boolean!
- explicitRoles : String!
- cursor : String
-}
-
-# Deliberately omitting password, authData, mfaSecret.
-type User {
- id: String!
- username: String!
- email: String!
- firstName: String!
- lastName: String!
- nickname: String!
- emailVerified: Boolean!
- isBot: Boolean!
- isGuest: Boolean!
- isSystemAdmin: Boolean!
- createAt: Float!
- updateAt: Float!
- deleteAt: Float!
- authService: String!
- customStatus: CustomStatus
- status: Status
- props: StringMap!
- notifyProps: StringMap!
- lastPictureUpdate: Float!
- lastPasswordUpdate: Float!
- failedAttempts: Float!
- locale: String!
- timezone: StringMap!
- position: String!
- mfaActive: Boolean!
- allowMarketing: Boolean!
- remoteId: String
- lastActivityAt: Float!
- botDescription: String!
- botLastIconUpdate: Float!
- termsOfServiceId: String!
- termsOfServiceCreateAt: Float!
- disableWelcomeEmail: Boolean!
- roles: [Role]!
- preferences: [Preference!]!
- sessions: [Session]!
-}
-
-type CustomStatus {
- emoji: String!
- text: String!
- duration: String!
- expiresAt: Time!
-}
-
-type Status {
- status: String!
- manual: Boolean!
- lastActivityAt: Float!
- activeChannel: String!
- dndEndTime: Float!
-}
-
-type Role {
- id: String!
- name: String!
- displayName: String!
- description: String!
- createAt: Float!
- updateAt: Float!
- deleteAt: Float!
- permissions: [String!]!
- schemeManaged: Boolean!
- builtIn: Boolean!
-}
-
-type Preference {
- userId: String!
- category: String!
- name: String!
- value: String!
-}
-
-type Team {
- id: String!
- displayName : String!
- name : String!
- createAt : Float!
- updateAt : Float!
- deleteAt : Float!
- description : String!
- email : String!
- type : String!
- companyName : String!
- allowedDomains : String!
- inviteId : String!
- lastTeamIconUpdate: Float!
- groupConstrained: Boolean
- allowOpenInvite: Boolean!
- schemeId : String
- policyId : String
- cloudLimitsArchived: Boolean!
-}
-
-type TeamMember {
- team: Team
- user: User
- roles: [Role]!
- deleteAt: Float!
- schemeGuest: Boolean!
- schemeUser: Boolean!
- schemeAdmin: Boolean!
- explicitRoles: String!
-}
-
-type SidebarCategory {
- id: String!
- sorting: SidebarCategorySorting!
- type: SidebarCategoryType!
- displayName: String!
- muted: Boolean!
- collapsed: Boolean!
- teamId: String!
- channelIds: [String!]!
- sortOrder: Float!
-}
-
-# Deliberately leaving out teamMembers.
-type Session {
- id: String!
- token: String!
- createAt: Float!
- expiresAt: Float!
- lastActivityAt: Float!
- deviceId: String!
- roles: String!
- isOAuth: Boolean!
- expiredNotify: Boolean!
- props: StringMap!
- local: Boolean!
-}
diff --git a/server/channels/app/app_iface.go b/server/channels/app/app_iface.go
index e1be1c577e..f23302b946 100644
--- a/server/channels/app/app_iface.go
+++ b/server/channels/app/app_iface.go
@@ -626,7 +626,6 @@ type AppIface interface {
GetChannelsForScheme(scheme *model.Scheme, offset int, limit int) (model.ChannelList, *model.AppError)
GetChannelsForSchemePage(scheme *model.Scheme, page int, perPage int) (model.ChannelList, *model.AppError)
GetChannelsForTeamForUser(c request.CTX, teamID string, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, *model.AppError)
- GetChannelsForTeamForUserWithCursor(c request.CTX, teamID string, userID string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, *model.AppError)
GetChannelsForUser(c request.CTX, userID string, includeDeleted bool, lastDeleteAt, pageSize int, fromChannelID string) (model.ChannelList, *model.AppError)
GetChannelsMemberCount(c request.CTX, channelIDs []string) (map[string]int64, *model.AppError)
GetChannelsUserNotIn(c request.CTX, teamID string, userID string, offset int, limit int) (model.ChannelList, *model.AppError)
diff --git a/server/channels/app/channel.go b/server/channels/app/channel.go
index f4af8bdfd7..bfc1985451 100644
--- a/server/channels/app/channel.go
+++ b/server/channels/app/channel.go
@@ -1938,14 +1938,6 @@ func (a *App) GetChannelsForTeamForUser(c request.CTX, teamID string, userID str
return a.Srv().getChannelsForTeamForUser(c, teamID, userID, opts)
}
-func (a *App) GetChannelsForTeamForUserWithCursor(c request.CTX, teamID string, userID string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, *model.AppError) {
- list, err := a.Srv().Store().Channel().GetChannelsWithCursor(teamID, userID, opts, afterChannelID)
- if err != nil {
- return nil, model.NewAppError("GetChannelsForUser", "app.channel.get_channels.get.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
- }
- return list, nil
-}
-
func (a *App) GetChannelsForUser(c request.CTX, userID string, includeDeleted bool, lastDeleteAt, pageSize int, fromChannelID string) (model.ChannelList, *model.AppError) {
list, err := a.Srv().Store().Channel().GetChannelsByUser(userID, includeDeleted, lastDeleteAt, pageSize, fromChannelID)
if err != nil {
diff --git a/server/channels/app/opentracing/opentracing_layer.go b/server/channels/app/opentracing/opentracing_layer.go
index 66d2bb7090..1cef616724 100644
--- a/server/channels/app/opentracing/opentracing_layer.go
+++ b/server/channels/app/opentracing/opentracing_layer.go
@@ -5718,28 +5718,6 @@ func (a *OpenTracingAppLayer) GetChannelsForTeamForUser(c request.CTX, teamID st
return resultVar0, resultVar1
}
-func (a *OpenTracingAppLayer) GetChannelsForTeamForUserWithCursor(c request.CTX, teamID string, userID string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, *model.AppError) {
- origCtx := a.ctx
- span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetChannelsForTeamForUserWithCursor")
-
- 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.GetChannelsForTeamForUserWithCursor(c, teamID, userID, opts, afterChannelID)
-
- if resultVar1 != nil {
- span.LogFields(spanlog.Error(resultVar1))
- ext.Error.Set(span, true)
- }
-
- return resultVar0, resultVar1
-}
-
func (a *OpenTracingAppLayer) GetChannelsForUser(c request.CTX, userID string, includeDeleted bool, lastDeleteAt int, pageSize int, fromChannelID string) (model.ChannelList, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetChannelsForUser")
diff --git a/server/channels/store/layer_generators/main.go b/server/channels/store/layer_generators/main.go
index cfb95abdb8..dd92cc2603 100644
--- a/server/channels/store/layer_generators/main.go
+++ b/server/channels/store/layer_generators/main.go
@@ -294,7 +294,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", "*SidebarCategorySearchOpts":
+ case "*UserGetByIdsOpts", "*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))
@@ -308,7 +308,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", "*SidebarCategorySearchOpts":
+ case "*UserGetByIdsOpts", "*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/server/channels/store/opentracinglayer/opentracinglayer.go b/server/channels/store/opentracinglayer/opentracinglayer.go
index 49355a569d..fd86e98e62 100644
--- a/server/channels/store/opentracinglayer/opentracinglayer.go
+++ b/server/channels/store/opentracinglayer/opentracinglayer.go
@@ -1333,24 +1333,6 @@ func (s *OpenTracingLayerChannelStore) GetChannelsMemberCount(channelIDs []strin
return result, err
}
-func (s *OpenTracingLayerChannelStore) GetChannelsWithCursor(teamId string, userId string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, error) {
- origCtx := s.Root.Store.Context()
- span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetChannelsWithCursor")
- s.Root.Store.SetContext(newCtx)
- defer func() {
- s.Root.Store.SetContext(origCtx)
- }()
-
- defer span.Finish()
- result, err := s.ChannelStore.GetChannelsWithCursor(teamId, userId, opts, afterChannelID)
- if err != nil {
- span.LogFields(spanlog.Error(err))
- ext.Error.Set(span, true)
- }
-
- return result, err
-}
-
func (s *OpenTracingLayerChannelStore) GetChannelsWithTeamDataByIds(channelIds []string, includeDeleted bool) ([]*model.ChannelWithTeamData, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetChannelsWithTeamDataByIds")
@@ -1652,24 +1634,6 @@ func (s *OpenTracingLayerChannelStore) GetMembersForUser(teamID string, userID s
return result, err
}
-func (s *OpenTracingLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
- origCtx := s.Root.Store.Context()
- span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetMembersForUserWithCursor")
- s.Root.Store.SetContext(newCtx)
- defer func() {
- s.Root.Store.SetContext(origCtx)
- }()
-
- defer span.Finish()
- result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts)
- if err != nil {
- span.LogFields(spanlog.Error(err))
- ext.Error.Set(span, true)
- }
-
- return result, err
-}
-
func (s *OpenTracingLayerChannelStore) GetMembersForUserWithPagination(userID string, page int, perPage int) (model.ChannelMembersWithTeamData, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.GetMembersForUserWithPagination")
diff --git a/server/channels/store/retrylayer/retrylayer.go b/server/channels/store/retrylayer/retrylayer.go
index 9f58341f55..a71aba933e 100644
--- a/server/channels/store/retrylayer/retrylayer.go
+++ b/server/channels/store/retrylayer/retrylayer.go
@@ -1480,27 +1480,6 @@ func (s *RetryLayerChannelStore) GetChannelsMemberCount(channelIDs []string) (ma
}
-func (s *RetryLayerChannelStore) GetChannelsWithCursor(teamId string, userId string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, error) {
-
- tries := 0
- for {
- result, err := s.ChannelStore.GetChannelsWithCursor(teamId, userId, opts, afterChannelID)
- 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) GetChannelsWithTeamDataByIds(channelIds []string, includeDeleted bool) ([]*model.ChannelWithTeamData, error) {
tries := 0
@@ -1843,27 +1822,6 @@ func (s *RetryLayerChannelStore) GetMembersForUser(teamID string, userID string)
}
-func (s *RetryLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
-
- tries := 0
- for {
- result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, 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) GetMembersForUserWithPagination(userID string, page int, perPage int) (model.ChannelMembersWithTeamData, error) {
tries := 0
diff --git a/server/channels/store/sqlstore/channel_store.go b/server/channels/store/sqlstore/channel_store.go
index 60b2780ea7..78341b9602 100644
--- a/server/channels/store/sqlstore/channel_store.go
+++ b/server/channels/store/sqlstore/channel_store.go
@@ -1073,66 +1073,6 @@ func (s SqlChannelStore) GetChannels(teamId string, userId string, opts *model.C
return channels, nil
}
-func (s SqlChannelStore) GetChannelsWithCursor(teamId string, userId string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, error) {
- query := s.getQueryBuilder().
- Select("ch.*").
- From("Channels ch, ChannelMembers cm").
- Where(
- sq.And{
- sq.Expr("ch.Id = cm.ChannelId"),
- sq.Eq{"cm.UserId": userId},
- },
- ).
- OrderBy("ch.Id")
-
- if opts.PerPage != nil {
- // The limit is verified at the GraphQL layer.
- query = query.Limit(uint64(*opts.PerPage))
- }
-
- if afterChannelID != "" {
- query = query.Where(sq.Gt{"ch.Id": afterChannelID})
- }
-
- if teamId != "" {
- query = query.Where(sq.Or{
- sq.Eq{"ch.TeamId": teamId},
- sq.Eq{"ch.TeamId": ""},
- })
- }
-
- if opts.IncludeDeleted {
- if opts.LastDeleteAt != 0 {
- // We filter by non-archived, and archived >= a timestamp.
- query = query.Where(sq.Or{
- sq.Eq{"ch.DeleteAt": 0},
- sq.GtOrEq{"ch.DeleteAt": opts.LastDeleteAt},
- })
- }
- // If opts.LastDeleteAt is not set, we include everything. That means no filter is needed.
- } else {
- // Don't include archived channels.
- query = query.Where(sq.Eq{"ch.DeleteAt": 0})
- }
-
- if opts.LastUpdateAt > 0 {
- query = query.Where(sq.GtOrEq{"ch.UpdateAt": opts.LastUpdateAt})
- }
-
- channels := model.ChannelList{}
- sql, args, err := query.ToSql()
- if err != nil {
- return nil, errors.Wrapf(err, "getchannels_tosql")
- }
-
- err = s.GetReplicaX().Select(&channels, sql, args...)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to get channels with TeamId=%s and UserId=%s", teamId, userId)
- }
-
- return channels, nil
-}
-
func (s SqlChannelStore) GetChannelsByUser(userId string, includeDeleted bool, lastDeleteAt, pageSize int, fromChannelID string) (model.ChannelList, error) {
query := s.getQueryBuilder().
Select("Channels.*").
@@ -3050,86 +2990,6 @@ func (s SqlChannelStore) GetMembersForUser(teamID string, userID string) (model.
return dbMembers.ToModel(), nil
}
-func (s SqlChannelStore) GetMembersForUserWithCursor(userID, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
- query := s.getQueryBuilder().
- Select(
- "ChannelMembers.ChannelId",
- "ChannelMembers.UserId",
- "ChannelMembers.Roles",
- "ChannelMembers.LastViewedAt",
- "ChannelMembers.MsgCount",
- "ChannelMembers.MentionCount",
- "ChannelMembers.MentionCountRoot",
- "COALESCE(ChannelMembers.UrgentMentionCount, 0) AS UrgentMentionCount",
- "ChannelMembers.MsgCountRoot",
- "ChannelMembers.NotifyProps",
- "ChannelMembers.LastUpdateAt",
- "ChannelMembers.SchemeUser",
- "ChannelMembers.SchemeAdmin",
- "ChannelMembers.SchemeGuest",
- "TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole",
- "TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole",
- "TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole",
- "ChannelScheme.DefaultChannelGuestRole ChannelSchemeDefaultGuestRole",
- "ChannelScheme.DefaultChannelUserRole ChannelSchemeDefaultUserRole",
- "ChannelScheme.DefaultChannelAdminRole ChannelSchemeDefaultAdminRole").
- From("ChannelMembers").
- InnerJoin("Channels ON ChannelMembers.ChannelId = Channels.Id").
- LeftJoin("Schemes ChannelScheme ON Channels.SchemeId = ChannelScheme.Id").
- LeftJoin("Teams ON Channels.TeamId = Teams.Id").
- LeftJoin("Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id").
- Where(sq.Eq{
- "ChannelMembers.UserId": userID,
- "Channels.DeleteAt": 0,
- }).
- OrderBy("ChannelId, UserId ASC").
- // The limit is verified at the GraphQL layer.
- Limit(uint64(opts.Limit))
-
- if teamID != "" {
- if opts.ExcludeTeam {
- // Exclude this team and DM/GMs
- query = query.Where(sq.And{
- sq.NotEq{"Channels.TeamId": teamID},
- sq.NotEq{"Channels.TeamId": ""},
- })
- } else {
- // Include this team and DM/GMs
- query = query.Where(sq.Or{
- sq.Eq{"Channels.TeamId": teamID},
- sq.Eq{"Channels.TeamId": ""},
- })
- }
- }
-
- if opts.AfterChannel != "" && opts.AfterUser != "" {
- query = query.Where(sq.Or{
- sq.Gt{"ChannelMembers.ChannelId": opts.AfterChannel},
- sq.And{
- sq.Eq{"ChannelMembers.ChannelId": opts.AfterChannel},
- sq.Gt{"ChannelMembers.UserId": opts.AfterUser},
- },
- })
- }
-
- if opts.LastUpdateAt != 0 {
- query = query.Where(sq.GtOrEq{"ChannelMembers.LastUpdateAt": opts.LastUpdateAt})
- }
-
- queryString, args, err := query.ToSql()
- if err != nil {
- return nil, errors.Wrap(err, "getMembersForUserWithCursor_tosql")
- }
-
- dbMembers := channelMemberWithSchemeRolesList{}
- err = s.GetReplicaX().Select(&dbMembers, queryString, args...)
- if err != nil {
- return nil, errors.Wrapf(err, "failed to find ChannelMembers data with userId=%s", userID)
- }
-
- return dbMembers.ToModel(), nil
-}
-
func (s SqlChannelStore) GetMembersForUserWithPagination(userId string, page, perPage int) (model.ChannelMembersWithTeamData, error) {
dbMembers := channelMemberWithTeamWithSchemeRolesList{}
offset := page * perPage
diff --git a/server/channels/store/store.go b/server/channels/store/store.go
index 67621de6e5..1b60015ba5 100644
--- a/server/channels/store/store.go
+++ b/server/channels/store/store.go
@@ -201,7 +201,6 @@ type ChannelStore interface {
GetDeletedByName(team_id string, name string) (*model.Channel, error)
GetDeleted(team_id string, offset int, limit int, userID string) (model.ChannelList, error)
GetChannels(teamID, userID string, opts *model.ChannelSearchOpts) (model.ChannelList, error)
- GetChannelsWithCursor(teamId string, userId string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, error)
GetChannelsByUser(userID string, includeDeleted bool, lastDeleteAt, pageSize int, fromChannelID string) (model.ChannelList, error)
GetAllChannelMembersById(id string) ([]string, error)
GetAllChannels(page, perPage int, opts ChannelSearchOpts) (model.ChannelListWithTeamData, error)
@@ -256,7 +255,6 @@ type ChannelStore interface {
GetMembersForUser(teamID string, userID string) (model.ChannelMembers, error)
GetTeamMembersForChannel(channelID string) ([]string, error)
GetMembersForUserWithPagination(userID string, page, perPage int) (model.ChannelMembersWithTeamData, error)
- GetMembersForUserWithCursor(userID, teamID string, opts *ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error)
Autocomplete(userID, term string, includeDeleted, isGuest bool) (model.ChannelListWithTeamData, error)
AutocompleteInTeam(teamID, userID, term string, includeDeleted, isGuest bool) (model.ChannelList, error)
AutocompleteInTeamForSearch(teamID string, userID string, term string, includeDeleted bool) (model.ChannelList, error)
@@ -1079,16 +1077,6 @@ type ThreadMembershipOpts struct {
UpdateParticipants bool
}
-// ChannelMemberGraphQLSearchOpts contains the options for a graphQL query
-// to get the channel members.
-type ChannelMemberGraphQLSearchOpts struct {
- AfterChannel string
- AfterUser string
- Limit int
- LastUpdateAt int
- ExcludeTeam bool
-}
-
// PostReminderMetadata contains some info needed to send
// the reminder message to the user.
type PostReminderMetadata struct {
diff --git a/server/channels/store/storetest/channel_store.go b/server/channels/store/storetest/channel_store.go
index cd20674005..45d3212033 100644
--- a/server/channels/store/storetest/channel_store.go
+++ b/server/channels/store/storetest/channel_store.go
@@ -94,7 +94,6 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlStore) {
t.Run("RemoveMembers", func(t *testing.T) { testChannelRemoveMembers(t, ss) })
t.Run("ChannelDeleteMemberStore", func(t *testing.T) { testChannelDeleteMemberStore(t, ss) })
t.Run("GetChannels", func(t *testing.T) { testChannelStoreGetChannels(t, ss) })
- t.Run("GetChannelsWithCursor", func(t *testing.T) { testChannelStoreGetChannelsWithCursor(t, ss) })
t.Run("GetChannelsByUser", func(t *testing.T) { testChannelStoreGetChannelsByUser(t, ss) })
t.Run("GetAllChannels", func(t *testing.T) { testChannelStoreGetAllChannels(t, ss, s) })
t.Run("GetMoreChannels", func(t *testing.T) { testChannelStoreGetMoreChannels(t, ss) })
@@ -103,7 +102,6 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlStore) {
t.Run("GetPublicChannelsByIdsForTeam", func(t *testing.T) { testChannelStoreGetPublicChannelsByIdsForTeam(t, ss) })
t.Run("GetChannelCounts", func(t *testing.T) { testChannelStoreGetChannelCounts(t, ss) })
t.Run("GetMembersForUser", func(t *testing.T) { testChannelStoreGetMembersForUser(t, ss) })
- t.Run("GetMembersForUserWithCursor", func(t *testing.T) { testChannelStoreGetMembersForUserWithCursor(t, ss) })
t.Run("GetMembersForUserWithPagination", func(t *testing.T) { testChannelStoreGetMembersForUserWithPagination(t, ss) })
t.Run("CountPostsAfter", func(t *testing.T) { testCountPostsAfter(t, ss) })
t.Run("CountUrgentPostsAfter", func(t *testing.T) { testCountUrgentPostsAfter(t, ss) })
@@ -3607,163 +3605,6 @@ func testChannelStoreGetChannels(t *testing.T, ss store.Store) {
ss.Channel().InvalidateAllChannelMembersForUser(m1.UserId)
}
-func testChannelStoreGetChannelsWithCursor(t *testing.T, ss store.Store) {
- teamID := model.NewId()
- o1 := &model.Channel{}
- o1.TeamId = teamID
- o1.DisplayName = "Channel1"
- o1.Name = NewTestId()
- o1.Type = model.ChannelTypeOpen
- var nErr error
- o1, nErr = ss.Channel().Save(o1, -1)
- require.NoError(t, nErr)
-
- o2 := model.Channel{}
- o2.TeamId = teamID
- o2.DisplayName = "Channel2"
- o2.Name = NewTestId()
- o2.Type = model.ChannelTypeOpen
- _, nErr = ss.Channel().Save(&o2, -1)
- require.NoError(t, nErr)
-
- o3 := model.Channel{}
- o3.TeamId = teamID
- o3.DisplayName = "Channel3"
- o3.Name = NewTestId()
- o3.Type = model.ChannelTypeOpen
- _, nErr = ss.Channel().Save(&o3, -1)
- require.NoError(t, nErr)
-
- m1 := model.ChannelMember{}
- m1.ChannelId = o1.Id
- m1.UserId = model.NewId()
- m1.NotifyProps = model.GetDefaultChannelNotifyProps()
- _, err := ss.Channel().SaveMember(&m1)
- require.NoError(t, err)
-
- m2 := model.ChannelMember{}
- m2.ChannelId = o1.Id
- m2.UserId = model.NewId()
- m2.NotifyProps = model.GetDefaultChannelNotifyProps()
- _, err = ss.Channel().SaveMember(&m2)
- require.NoError(t, err)
-
- m3 := model.ChannelMember{}
- m3.ChannelId = o2.Id
- m3.UserId = m1.UserId
- m3.NotifyProps = model.GetDefaultChannelNotifyProps()
- _, err = ss.Channel().SaveMember(&m3)
- require.NoError(t, err)
-
- m4 := model.ChannelMember{}
- m4.ChannelId = o3.Id
- m4.UserId = m1.UserId
- m4.NotifyProps = model.GetDefaultChannelNotifyProps()
- _, err = ss.Channel().SaveMember(&m4)
- require.NoError(t, err)
-
- list, nErr := ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: false,
- LastDeleteAt: 0,
- PerPage: model.NewInt(2),
- }, "")
- require.NoError(t, nErr)
- require.Len(t, list, 2)
- require.Equal(t, teamID, list[0].TeamId, "incorrect teamID")
- require.Equal(t, teamID, list[1].TeamId, "incorrect teamID")
-
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: false,
- LastDeleteAt: 0,
- PerPage: model.NewInt(2),
- }, list[1].Id)
- require.NoError(t, nErr)
- require.Len(t, list, 1)
- require.Equal(t, teamID, list[0].TeamId, "incorrect teamID")
-
- // all channels should be returned
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: false,
- LastDeleteAt: 0,
- }, "")
- require.NoError(t, nErr)
- require.Len(t, list, 3)
-
- // should return empty list
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: false,
- LastDeleteAt: 0,
- }, list[2].Id)
- require.NoError(t, nErr)
- require.Len(t, list, 0)
-
- // Sleeping to guarantee that the
- // UpdateAt is different.
- // The proper way would be to set UpdateAt during channel creation itself,
- // but the *Channel.PreSave method ignores any existing CreateAt value.
- // TODO: check if using an existing CreateAt breaks anything.
- time.Sleep(time.Millisecond)
-
- now := model.GetMillis()
- _, nErr = ss.Channel().Update(o1)
- require.NoError(t, nErr)
-
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: false,
- LastUpdateAt: int(now),
- }, "")
- require.NoError(t, nErr)
- // should return 1
- require.Len(t, list, 1)
-
- nErr = ss.Channel().Delete(o2.Id, 10)
- require.NoError(t, nErr)
-
- nErr = ss.Channel().Delete(o3.Id, 20)
- require.NoError(t, nErr)
-
- // should return 1
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: false,
- LastDeleteAt: 0,
- }, "")
- require.NoError(t, nErr)
- require.Len(t, list, 1)
-
- // Should return all
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: true,
- LastDeleteAt: 0,
- PerPage: model.NewInt(2),
- }, "")
- require.NoError(t, nErr)
- require.Len(t, list, 2)
-
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: true,
- LastDeleteAt: 0,
- PerPage: model.NewInt(2),
- }, list[1].Id)
- require.NoError(t, nErr)
- require.Len(t, list, 1)
-
- // Should still return all
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: true,
- LastDeleteAt: 10,
- }, "")
- require.NoError(t, nErr)
- require.Len(t, list, 3)
-
- // Should return 2
- list, nErr = ss.Channel().GetChannelsWithCursor(o1.TeamId, m1.UserId, &model.ChannelSearchOpts{
- IncludeDeleted: true,
- LastDeleteAt: 20,
- }, "")
- require.NoError(t, nErr)
- require.Len(t, list, 2)
-}
-
func testChannelStoreGetChannelsByUser(t *testing.T, ss store.Store) {
team := model.NewId()
team2 := model.NewId()
@@ -4562,183 +4403,6 @@ func testChannelStoreGetMembersForUser(t *testing.T, ss store.Store) {
})
}
-func testChannelStoreGetMembersForUserWithCursor(t *testing.T, ss store.Store) {
- t1 := model.Team{}
- t1.DisplayName = "Team1"
- t1.Name = NewTestId()
- t1.Email = MakeEmail()
- t1.Type = model.TeamOpen
- _, err := ss.Team().Save(&t1)
- require.NoError(t, err)
-
- t2 := model.Team{}
- t2.DisplayName = "Team2"
- t2.Name = NewTestId()
- t2.Email = MakeEmail()
- t2.Type = model.TeamOpen
- _, err = ss.Team().Save(&t2)
- require.NoError(t, err)
-
- o1 := model.Channel{}
- o1.TeamId = t1.Id
- o1.DisplayName = "Channel1"
- o1.Name = NewTestId()
- o1.Type = model.ChannelTypeOpen
- _, nErr := ss.Channel().Save(&o1, -1)
- require.NoError(t, nErr)
-
- o2 := model.Channel{}
- o2.TeamId = o1.TeamId
- o2.DisplayName = "Channel2"
- o2.Name = NewTestId()
- o2.Type = model.ChannelTypeOpen
- _, nErr = ss.Channel().Save(&o2, -1)
- require.NoError(t, nErr)
-
- o3 := model.Channel{}
- o3.TeamId = t2.Id
- o3.DisplayName = "Channel3"
- o3.Name = NewTestId()
- o3.Type = model.ChannelTypeOpen
- _, nErr = ss.Channel().Save(&o3, -1)
- require.NoError(t, nErr)
-
- m1 := model.ChannelMember{}
- m1.ChannelId = o1.Id
- m1.UserId = model.NewId()
- m1.NotifyProps = model.GetDefaultChannelNotifyProps()
- _, err = ss.Channel().SaveMember(&m1)
- require.NoError(t, err)
-
- m2 := model.ChannelMember{}
- m2.ChannelId = o2.Id
- m2.UserId = m1.UserId
- m2.NotifyProps = model.GetDefaultChannelNotifyProps()
- _, err = ss.Channel().SaveMember(&m2)
- require.NoError(t, err)
-
- m3 := model.ChannelMember{}
- m3.ChannelId = o3.Id
- m3.UserId = m1.UserId
- m3.NotifyProps = model.GetDefaultChannelNotifyProps()
- _, err = ss.Channel().SaveMember(&m3)
- require.NoError(t, err)
-
- t.Run("with channels", func(t *testing.T) {
- var members model.ChannelMembers
- opts := &store.ChannelMemberGraphQLSearchOpts{
- Limit: 1,
- }
- members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err)
- assert.Len(t, members, 1)
- opts.Limit = 3
- members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err)
- assert.Len(t, members, 3)
- opts.AfterChannel = members[0].ChannelId
- opts.AfterUser = m1.UserId
- opts.Limit = 1
- members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err)
- assert.Len(t, members, 1)
- })
-
- t.Run("with channels and direct messages", func(t *testing.T) {
- user := model.User{Id: m1.UserId}
- u1 := model.User{Id: model.NewId()}
- u2 := model.User{Id: model.NewId()}
- u3 := model.User{Id: model.NewId()}
- u4 := model.User{Id: model.NewId()}
- _, nErr = ss.Channel().CreateDirectChannel(&u1, &user)
- require.NoError(t, nErr)
- _, nErr = ss.Channel().CreateDirectChannel(&u2, &user)
- require.NoError(t, nErr)
- // other user direct message
- _, nErr = ss.Channel().CreateDirectChannel(&u3, &u4)
- require.NoError(t, nErr)
-
- opts := &store.ChannelMemberGraphQLSearchOpts{
- Limit: 10,
- }
- members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err2)
- assert.Len(t, members, 5)
-
- opts.Limit = 2
- members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err2)
- assert.Len(t, members, 2)
-
- opts.AfterChannel = members[1].ChannelId
- opts.AfterUser = m1.UserId
- opts.Limit = 2
- members, err2 = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err2)
- assert.Len(t, members, 2)
- })
-
- t.Run("for a specific team", func(t *testing.T) {
- opts := &store.ChannelMemberGraphQLSearchOpts{
- Limit: 10,
- }
- members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, t2.Id, opts)
- require.NoError(t, err2)
- assert.Len(t, members, 3)
- })
-
- t.Run("excluding a team", func(t *testing.T) {
- opts := &store.ChannelMemberGraphQLSearchOpts{
- Limit: 10,
- ExcludeTeam: true,
- }
- members, err2 := ss.Channel().GetMembersForUserWithCursor(m1.UserId, t2.Id, opts)
- require.NoError(t, err2)
- assert.Len(t, members, 2)
- })
-
- t.Run("with channels, direct channels and group messages", func(t *testing.T) {
- userIds := []string{model.NewId(), model.NewId(), model.NewId(), m1.UserId}
- group := &model.Channel{
- Name: model.GetGroupNameFromUserIds(userIds),
- DisplayName: "test",
- Type: model.ChannelTypeGroup,
- }
- var channel *model.Channel
- channel, nErr = ss.Channel().Save(group, 10000)
- require.NoError(t, nErr)
- for _, userId := range userIds {
- cm := &model.ChannelMember{
- UserId: userId,
- ChannelId: channel.Id,
- NotifyProps: model.GetDefaultChannelNotifyProps(),
- SchemeUser: true,
- }
-
- _, err = ss.Channel().SaveMember(cm)
- require.NoError(t, err)
- }
- opts := &store.ChannelMemberGraphQLSearchOpts{
- Limit: 10,
- }
- members, err := ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err)
- assert.Len(t, members, 6)
-
- opts.Limit = 2
- members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err)
- assert.Len(t, members, 2)
-
- opts.AfterChannel = members[1].ChannelId
- opts.AfterUser = m1.UserId
- opts.Limit = 10
- members, err = ss.Channel().GetMembersForUserWithCursor(m1.UserId, "", opts)
- require.NoError(t, err)
- assert.Len(t, members, 4)
- })
-}
-
func testChannelStoreGetMembersForUserWithPagination(t *testing.T, ss store.Store) {
t1 := model.Team{
DisplayName: "team1",
diff --git a/server/channels/store/storetest/channel_store_categories.go b/server/channels/store/storetest/channel_store_categories.go
index ec529fd420..7af70208f1 100644
--- a/server/channels/store/storetest/channel_store_categories.go
+++ b/server/channels/store/storetest/channel_store_categories.go
@@ -435,50 +435,6 @@ func testCreateInitialSidebarCategories(t *testing.T, ss store.Store) {
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(c, 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) {
diff --git a/server/channels/store/storetest/mocks/ChannelStore.go b/server/channels/store/storetest/mocks/ChannelStore.go
index cea58b35c2..53bb50726e 100644
--- a/server/channels/store/storetest/mocks/ChannelStore.go
+++ b/server/channels/store/storetest/mocks/ChannelStore.go
@@ -976,32 +976,6 @@ func (_m *ChannelStore) GetChannelsMemberCount(channelIDs []string) (map[string]
return r0, r1
}
-// GetChannelsWithCursor provides a mock function with given fields: teamId, userId, opts, afterChannelID
-func (_m *ChannelStore) GetChannelsWithCursor(teamId string, userId string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, error) {
- ret := _m.Called(teamId, userId, opts, afterChannelID)
-
- var r0 model.ChannelList
- var r1 error
- if rf, ok := ret.Get(0).(func(string, string, *model.ChannelSearchOpts, string) (model.ChannelList, error)); ok {
- return rf(teamId, userId, opts, afterChannelID)
- }
- if rf, ok := ret.Get(0).(func(string, string, *model.ChannelSearchOpts, string) model.ChannelList); ok {
- r0 = rf(teamId, userId, opts, afterChannelID)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(model.ChannelList)
- }
- }
-
- if rf, ok := ret.Get(1).(func(string, string, *model.ChannelSearchOpts, string) error); ok {
- r1 = rf(teamId, userId, opts, afterChannelID)
- } else {
- r1 = ret.Error(1)
- }
-
- return r0, r1
-}
-
// GetChannelsWithTeamDataByIds provides a mock function with given fields: channelIds, includeDeleted
func (_m *ChannelStore) GetChannelsWithTeamDataByIds(channelIds []string, includeDeleted bool) ([]*model.ChannelWithTeamData, error) {
ret := _m.Called(channelIds, includeDeleted)
@@ -1444,32 +1418,6 @@ func (_m *ChannelStore) GetMembersForUser(teamID string, userID string) (model.C
return r0, r1
}
-// GetMembersForUserWithCursor provides a mock function with given fields: userID, teamID, opts
-func (_m *ChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
- ret := _m.Called(userID, teamID, opts)
-
- var r0 model.ChannelMembers
- var r1 error
- if rf, ok := ret.Get(0).(func(string, string, *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error)); ok {
- return rf(userID, teamID, opts)
- }
- if rf, ok := ret.Get(0).(func(string, string, *store.ChannelMemberGraphQLSearchOpts) model.ChannelMembers); ok {
- r0 = rf(userID, teamID, opts)
- } else {
- if ret.Get(0) != nil {
- r0 = ret.Get(0).(model.ChannelMembers)
- }
- }
-
- if rf, ok := ret.Get(1).(func(string, string, *store.ChannelMemberGraphQLSearchOpts) error); ok {
- r1 = rf(userID, teamID, opts)
- } else {
- r1 = ret.Error(1)
- }
-
- return r0, r1
-}
-
// GetMembersForUserWithPagination provides a mock function with given fields: userID, page, perPage
func (_m *ChannelStore) GetMembersForUserWithPagination(userID string, page int, perPage int) (model.ChannelMembersWithTeamData, error) {
ret := _m.Called(userID, page, perPage)
diff --git a/server/channels/store/timerlayer/timerlayer.go b/server/channels/store/timerlayer/timerlayer.go
index 2c2a20cdaf..ab90339dac 100644
--- a/server/channels/store/timerlayer/timerlayer.go
+++ b/server/channels/store/timerlayer/timerlayer.go
@@ -1243,22 +1243,6 @@ func (s *TimerLayerChannelStore) GetChannelsMemberCount(channelIDs []string) (ma
return result, err
}
-func (s *TimerLayerChannelStore) GetChannelsWithCursor(teamId string, userId string, opts *model.ChannelSearchOpts, afterChannelID string) (model.ChannelList, error) {
- start := time.Now()
-
- result, err := s.ChannelStore.GetChannelsWithCursor(teamId, userId, opts, afterChannelID)
-
- 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.GetChannelsWithCursor", success, elapsed)
- }
- return result, err
-}
-
func (s *TimerLayerChannelStore) GetChannelsWithTeamDataByIds(channelIds []string, includeDeleted bool) ([]*model.ChannelWithTeamData, error) {
start := time.Now()
@@ -1531,22 +1515,6 @@ func (s *TimerLayerChannelStore) GetMembersForUser(teamID string, userID string)
return result, err
}
-func (s *TimerLayerChannelStore) GetMembersForUserWithCursor(userID string, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
- start := time.Now()
-
- result, err := s.ChannelStore.GetMembersForUserWithCursor(userID, teamID, opts)
-
- 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.GetMembersForUserWithCursor", success, elapsed)
- }
- return result, err
-}
-
func (s *TimerLayerChannelStore) GetMembersForUserWithPagination(userID string, page int, perPage int) (model.ChannelMembersWithTeamData, error) {
start := time.Now()
diff --git a/server/channels/web/context.go b/server/channels/web/context.go
index cdea7d5752..d1890d2598 100644
--- a/server/channels/web/context.go
+++ b/server/channels/web/context.go
@@ -19,15 +19,12 @@ import (
)
type Context struct {
- App app.AppIface
- AppContext *request.Context
- Logger *mlog.Logger
- Params *Params
- Err *model.AppError
- // This is used to track the graphQL query that's being executed,
- // so that we can monitor the timings in Grafana.
- GraphQLOperationName string
- siteURLHeader string
+ App app.AppIface
+ AppContext *request.Context
+ Logger *mlog.Logger
+ Params *Params
+ Err *model.AppError
+ siteURLHeader string
}
// LogAuditRec logs an audit record using default LevelAPI.
diff --git a/server/channels/web/handlers.go b/server/channels/web/handlers.go
index 05b0e87982..0adb2c6aac 100644
--- a/server/channels/web/handlers.go
+++ b/server/channels/web/handlers.go
@@ -408,14 +408,7 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != model.APIURLSuffix+"/websocket" {
elapsed := float64(time.Since(now)) / float64(time.Second)
- var endpoint string
- if strings.HasPrefix(r.URL.Path, model.APIURLSuffixV5) {
- // It's a graphQL query, so use the operation name.
- endpoint = c.GraphQLOperationName
- } else {
- endpoint = h.HandlerName
- }
- c.App.Metrics().ObserveAPIEndpointDuration(endpoint, r.Method, statusCode, elapsed)
+ c.App.Metrics().ObserveAPIEndpointDuration(h.HandlerName, r.Method, statusCode, elapsed)
}
}
}
diff --git a/server/go.mod b/server/go.mod
index 5b23edda01..cec181adfc 100644
--- a/server/go.mod
+++ b/server/go.mod
@@ -25,8 +25,6 @@ require (
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
github.com/gorilla/websocket v1.5.0
- github.com/graph-gophers/dataloader/v6 v6.0.0
- github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a
github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c
github.com/hashicorp/go-multierror v1.1.1
github.com/icrowley/fake v0.0.0-20221112152111-d7b7e2276db2
@@ -121,6 +119,7 @@ require (
github.com/google/uuid v1.3.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/gorilla/css v1.0.0 // indirect
+ github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-plugin v1.4.10 // indirect
diff --git a/server/go.sum b/server/go.sum
index 908ad8ac08..65a761dc0f 100644
--- a/server/go.sum
+++ b/server/go.sum
@@ -345,8 +345,6 @@ github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/graph-gophers/dataloader/v6 v6.0.0 h1:qBpmq3B8PIQesoh0EJXKGfw+ulMUb+KFl4IZOe9ScWg=
-github.com/graph-gophers/dataloader/v6 v6.0.0/go.mod h1:J15OZSnOoZgMkijpbZcwCmglIDYqlUiTEE1xLPbyqZM=
github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a h1:i0+Se9S+2zL5CBxJouqn2Ej6UQMwH1c57ZB6DVnqck4=
github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
@@ -520,7 +518,6 @@ github.com/otiai10/gosseract/v2 v2.4.0/go.mod h1:fhbIDRh29bj13vni6RT3gtWKjKCAeqD
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.3 h1:7JgpsBaN0uMkyju4tbYHu0mnM55hNKVYLsXmwr15NQI=
github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
-github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
diff --git a/server/public/go.mod b/server/public/go.mod
index 3c9e32c478..f046e3744a 100644
--- a/server/public/go.mod
+++ b/server/public/go.mod
@@ -10,7 +10,6 @@ require (
github.com/golang/mock v1.6.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
- github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a
github.com/hashicorp/go-hclog v1.5.0
github.com/hashicorp/go-plugin v1.4.10
github.com/lib/pq v1.10.9
diff --git a/server/public/go.sum b/server/public/go.sum
index 5c6104dd0f..b079121912 100644
--- a/server/public/go.sum
+++ b/server/public/go.sum
@@ -40,9 +40,6 @@ github.com/go-asn1-ber/asn1-ber v1.3.2-0.20191121212151-29be175fc3a3/go.mod h1:h
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -60,7 +57,6 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -76,8 +72,6 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a h1:i0+Se9S+2zL5CBxJouqn2Ej6UQMwH1c57ZB6DVnqck4=
-github.com/graph-gophers/graphql-go v1.5.1-0.20230110080634-edea822f558a/go.mod h1:YtmJZDLbF1YYNrlNAuiO5zAStUWc3XZT07iGsVqe1Os=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
@@ -131,7 +125,6 @@ github.com/nicksnyder/go-i18n/v2 v2.0.3 h1:ks/JkQiOEhhuF6jpNvx+Wih1NIiXzUnZeZVnJ
github.com/nicksnyder/go-i18n/v2 v2.0.3/go.mod h1:oDab7q8XCYMRlcrBnaY/7B1eOectbvj6B1UPBT+p5jo=
github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
-github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
@@ -190,7 +183,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -222,8 +214,6 @@ github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63M
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
-go.opentelemetry.io/otel v1.6.3/go.mod h1:7BgNga5fNlF/iZjG06hM3yofffp0ofKCDwSXx1GC4dI=
-go.opentelemetry.io/otel/trace v1.6.3/go.mod h1:GNJQusJlUgZl9/TQBPKU/Y/ty+0iVB5fjhKeJGZPGFs=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
diff --git a/server/public/model/channel.go b/server/public/model/channel.go
index 169d87760b..eaf3c79018 100644
--- a/server/public/model/channel.go
+++ b/server/public/model/channel.go
@@ -7,7 +7,6 @@ import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
- "errors"
"io"
"net/http"
"regexp"
@@ -201,47 +200,6 @@ func WithID(ID string) ChannelOption {
}
}
-// The following are some GraphQL methods necessary to return the
-// data in float64 type. The spec doesn't support 64 bit integers,
-// so we have to pass the data in float64. The _ at the end is
-// a hack to keep the attribute name same in GraphQL schema.
-
-func (o *Channel) CreateAt_() float64 {
- return float64(o.CreateAt)
-}
-
-func (o *Channel) UpdateAt_() float64 {
- return float64(o.UpdateAt)
-}
-
-func (o *Channel) DeleteAt_() float64 {
- return float64(o.DeleteAt)
-}
-
-func (o *Channel) LastPostAt_() float64 {
- return float64(o.LastPostAt)
-}
-
-func (o *Channel) TotalMsgCount_() float64 {
- return float64(o.TotalMsgCount)
-}
-
-func (o *Channel) TotalMsgCountRoot_() float64 {
- return float64(o.TotalMsgCountRoot)
-}
-
-func (o *Channel) LastRootPostAt_() float64 {
- return float64(o.LastRootPostAt)
-}
-
-func (o *Channel) ExtraUpdateAt_() float64 {
- return float64(o.ExtraUpdateAt)
-}
-
-func (o *Channel) Props_() StringInterface {
- return StringInterface(o.Props)
-}
-
func (o *Channel) DeepCopy() *Channel {
cCopy := *o
if cCopy.SchemeId != nil {
@@ -391,24 +349,10 @@ func (o *Channel) GetOtherUserIdForDM(userId string) string {
return otherUserId
}
-func (ChannelType) ImplementsGraphQLType(name string) bool {
- return name == "ChannelType"
-}
-
func (t ChannelType) MarshalJSON() ([]byte, error) {
return json.Marshal(string(t))
}
-func (t *ChannelType) UnmarshalGraphQL(input any) error {
- chType, ok := input.(string)
- if !ok {
- return errors.New("wrong type")
- }
-
- *t = ChannelType(chType)
- return nil
-}
-
func GetDMNameFromIds(userId1, userId2 string) string {
if userId1 > userId2 {
return userId2 + "__" + userId1
diff --git a/server/public/model/channel_member.go b/server/public/model/channel_member.go
index 8201fda346..584534447b 100644
--- a/server/public/model/channel_member.go
+++ b/server/public/model/channel_member.go
@@ -88,39 +88,6 @@ func (o *ChannelMember) Auditable() map[string]interface{} {
}
}
-// The following are some GraphQL methods necessary to return the
-// data in float64 type. The spec doesn't support 64 bit integers,
-// so we have to pass the data in float64. The _ at the end is
-// a hack to keep the attribute name same in GraphQL schema.
-
-func (o *ChannelMember) LastViewedAt_() float64 {
- return float64(o.LastViewedAt)
-}
-
-func (o *ChannelMember) MsgCount_() float64 {
- return float64(o.MsgCount)
-}
-
-func (o *ChannelMember) MentionCount_() float64 {
- return float64(o.MentionCount)
-}
-
-func (o *ChannelMember) MentionCountRoot_() float64 {
- return float64(o.MentionCountRoot)
-}
-
-func (o *ChannelMember) UrgentMentionCount_() float64 {
- return float64(o.UrgentMentionCount)
-}
-
-func (o *ChannelMember) MsgCountRoot_() float64 {
- return float64(o.MsgCountRoot)
-}
-
-func (o *ChannelMember) LastUpdateAt_() float64 {
- return float64(o.LastUpdateAt)
-}
-
// ChannelMemberWithTeamData contains ChannelMember appended with extra team information
// as well.
type ChannelMemberWithTeamData struct {
diff --git a/server/public/model/channel_sidebar.go b/server/public/model/channel_sidebar.go
index e8b5a6a215..8547fc3262 100644
--- a/server/public/model/channel_sidebar.go
+++ b/server/public/model/channel_sidebar.go
@@ -5,7 +5,6 @@ package model
import (
"encoding/json"
- "errors"
"regexp"
)
@@ -89,42 +88,10 @@ func IsValidCategoryId(s string) bool {
return categoryIdPattern.MatchString(s)
}
-func (SidebarCategoryType) ImplementsGraphQLType(name string) bool {
- return name == "SidebarCategoryType"
-}
-
func (t SidebarCategoryType) MarshalJSON() ([]byte, error) {
return json.Marshal(string(t))
}
-func (t *SidebarCategoryType) UnmarshalGraphQL(input any) error {
- chType, ok := input.(string)
- if !ok {
- return errors.New("wrong type")
- }
-
- *t = SidebarCategoryType(chType)
- return nil
-}
-
-func (SidebarCategorySorting) ImplementsGraphQLType(name string) bool {
- return name == "SidebarCategorySorting"
-}
-
func (t SidebarCategorySorting) MarshalJSON() ([]byte, error) {
return json.Marshal(string(t))
}
-
-func (t *SidebarCategorySorting) UnmarshalGraphQL(input any) error {
- chType, ok := input.(string)
- if !ok {
- return errors.New("wrong type")
- }
-
- *t = SidebarCategorySorting(chType)
- return nil
-}
-
-func (t *SidebarCategory) SortOrder_() float64 {
- return float64(t.SortOrder)
-}
diff --git a/server/public/model/custom_status.go b/server/public/model/custom_status.go
index 74cd924047..2f5084b867 100644
--- a/server/public/model/custom_status.go
+++ b/server/public/model/custom_status.go
@@ -8,8 +8,6 @@ import (
"encoding/json"
"fmt"
"time"
-
- "github.com/graph-gophers/graphql-go"
)
const (
@@ -63,12 +61,6 @@ func (cs *CustomStatus) AreDurationAndExpirationTimeValid() bool {
return false
}
-// ExpiresAt_ returns the time in a type that has the marshal/unmarshal methods
-// attached to it.
-func (cs *CustomStatus) ExpiresAt_() graphql.Time {
- return graphql.Time{Time: cs.ExpiresAt}
-}
-
func RuneToHexadecimalString(r rune) string {
return fmt.Sprintf("%04x", r)
}
diff --git a/server/public/model/feature_flags.go b/server/public/model/feature_flags.go
index 3a2a470f62..bceb4daa38 100644
--- a/server/public/model/feature_flags.go
+++ b/server/public/model/feature_flags.go
@@ -29,9 +29,6 @@ type FeatureFlags struct {
NormalizeLdapDNs bool
- // Enable GraphQL feature
- GraphQL bool
-
PostPriority bool
// Enable WYSIWYG text editor
@@ -54,7 +51,6 @@ func (f *FeatureFlags) SetDefaults() {
f.EnableRemoteClusterService = false
f.AppsEnabled = true
f.NormalizeLdapDNs = false
- f.GraphQL = false
f.CallsEnabled = true
f.DeprecateCloudFree = false
f.WysiwygEditor = false
diff --git a/server/public/model/status.go b/server/public/model/status.go
index e362afe844..a0c5d073f3 100644
--- a/server/public/model/status.go
+++ b/server/public/model/status.go
@@ -34,19 +34,6 @@ func (s *Status) ToJSON() ([]byte, error) {
return json.Marshal(sCopy)
}
-// The following are some GraphQL methods necessary to return the
-// data in float64 type. The spec doesn't support 64 bit integers,
-// so we have to pass the data in float64. The _ at the end is
-// a hack to keep the attribute name same in GraphQL schema.
-
-func (s *Status) LastActivityAt_() float64 {
- return float64(s.LastActivityAt)
-}
-
-func (s *Status) DNDEndTime_() float64 {
- return float64(s.DNDEndTime)
-}
-
func StatusListToJSON(u []*Status) ([]byte, error) {
list := make([]Status, len(u))
for i, s := range u {
diff --git a/server/public/model/team.go b/server/public/model/team.go
index 7643051688..f6b8ca71c4 100644
--- a/server/public/model/team.go
+++ b/server/public/model/team.go
@@ -287,24 +287,3 @@ func (o *Team) ShallowCopy() *Team {
c := *o
return &c
}
-
-// The following are some GraphQL methods necessary to return the
-// data in float64 type. The spec doesn't support 64 bit integers,
-// so we have to pass the data in float64. The _ at the end is
-// a hack to keep the attribute name same in GraphQL schema.
-
-func (o *Team) CreateAt_() float64 {
- return float64(o.UpdateAt)
-}
-
-func (o *Team) UpdateAt_() float64 {
- return float64(o.UpdateAt)
-}
-
-func (o *Team) DeleteAt_() float64 {
- return float64(o.DeleteAt)
-}
-
-func (o *Team) LastTeamIconUpdate_() float64 {
- return float64(o.LastTeamIconUpdate)
-}
diff --git a/server/public/model/team_member.go b/server/public/model/team_member.go
index fbcecadd73..276777eb04 100644
--- a/server/public/model/team_member.go
+++ b/server/public/model/team_member.go
@@ -142,9 +142,3 @@ func (o *TeamMember) PreUpdate() {
func (o *TeamMember) GetRoles() []string {
return strings.Fields(o.Roles)
}
-
-// DeleteAt_ returns the deleteAt value in float64. This is necessary to work
-// with GraphQL since it doesn't support 64 bit integers.
-func (o *TeamMember) DeleteAt_() float64 {
- return float64(o.DeleteAt)
-}
diff --git a/server/public/model/user.go b/server/public/model/user.go
index daf8c77cdd..baf28be7bc 100644
--- a/server/public/model/user.go
+++ b/server/public/model/user.go
@@ -468,47 +468,6 @@ func (u *User) PreSave() {
}
}
-// The following are some GraphQL methods necessary to return the
-// data in float64 type. The spec doesn't support 64 bit integers,
-// so we have to pass the data in float64. The _ at the end is
-// a hack to keep the attribute name same in GraphQL schema.
-
-func (u *User) CreateAt_() float64 {
- return float64(u.CreateAt)
-}
-
-func (u *User) DeleteAt_() float64 {
- return float64(u.DeleteAt)
-}
-
-func (u *User) UpdateAt_() float64 {
- return float64(u.UpdateAt)
-}
-
-func (u *User) LastPictureUpdate_() float64 {
- return float64(u.LastPictureUpdate)
-}
-
-func (u *User) LastPasswordUpdate_() float64 {
- return float64(u.LastPasswordUpdate)
-}
-
-func (u *User) FailedAttempts_() float64 {
- return float64(u.FailedAttempts)
-}
-
-func (u *User) LastActivityAt_() float64 {
- return float64(u.LastActivityAt)
-}
-
-func (u *User) BotLastIconUpdate_() float64 {
- return float64(u.BotLastIconUpdate)
-}
-
-func (u *User) TermsOfServiceCreateAt_() float64 {
- return float64(u.TermsOfServiceCreateAt)
-}
-
// PreUpdate should be run before updating the user in the db.
func (u *User) PreUpdate() {
u.Username = SanitizeUnicode(u.Username)
diff --git a/server/public/model/utils.go b/server/public/model/utils.go
index 621e00428c..347cbaf09f 100644
--- a/server/public/model/utils.go
+++ b/server/public/model/utils.go
@@ -177,24 +177,10 @@ func (m StringMap) Value() (driver.Value, error) {
return string(buf), nil
}
-func (StringMap) ImplementsGraphQLType(name string) bool {
- return name == "StringMap"
-}
-
func (m StringMap) MarshalJSON() ([]byte, error) {
return json.Marshal((map[string]string)(m))
}
-func (m *StringMap) UnmarshalGraphQL(input any) error {
- json, ok := input.(map[string]string)
- if !ok {
- return errors.New("wrong type")
- }
-
- *m = json
- return nil
-}
-
func (si *StringInterface) Scan(value any) error {
if value == nil {
return nil
@@ -228,24 +214,10 @@ func (si StringInterface) Value() (driver.Value, error) {
return string(j), err
}
-func (StringInterface) ImplementsGraphQLType(name string) bool {
- return name == "StringInterface"
-}
-
func (si StringInterface) MarshalJSON() ([]byte, error) {
return json.Marshal((map[string]any)(si))
}
-func (si *StringInterface) UnmarshalGraphQL(input any) error {
- json, ok := input.(map[string]any)
- if !ok {
- return errors.New("wrong type")
- }
-
- *si = json
- return nil
-}
-
var translateFunc i18n.TranslateFunc
var translateFuncOnce sync.Once
diff --git a/server/public/pluginapi/experimental/oauther/mock_oauther/mock_oauther.go b/server/public/pluginapi/experimental/oauther/mock_oauther/mock_oauther.go
index 22c1f8d594..c2253d2f11 100644
--- a/server/public/pluginapi/experimental/oauther/mock_oauther/mock_oauther.go
+++ b/server/public/pluginapi/experimental/oauther/mock_oauther/mock_oauther.go
@@ -5,9 +5,10 @@
package mock_oauther
import (
+ reflect "reflect"
+
gomock "github.com/golang/mock/gomock"
oauth2 "golang.org/x/oauth2"
- reflect "reflect"
)
// MockOAuther is a mock of OAuther interface
diff --git a/server/public/shared/mlog/graphql.go b/server/public/shared/mlog/graphql.go
deleted file mode 100644
index 7f439f3010..0000000000
--- a/server/public/shared/mlog/graphql.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
-// See LICENSE.txt for license information.
-
-package mlog
-
-import (
- "context"
-)
-
-// GraphQLLogger is used to log panics that occur during query execution.
-type GraphQLLogger struct {
- logger *Logger
-}
-
-func NewGraphQLLogger(logger *Logger) *GraphQLLogger {
- return &GraphQLLogger{logger: logger}
-}
-
-// LogPanic satisfies the graphql/log.Logger interface.
-// It converts the panic into an error.
-func (l *GraphQLLogger) LogPanic(_ context.Context, value any) {
- l.logger.Error("Error while executing GraphQL query", Any("error", value))
-}