[MM-57356] Make use of go1.21 features (#26620)

This commit is contained in:
Ben Schumacher 2024-04-04 13:44:03 +02:00 committed by GitHub
parent 3c45c44cb1
commit 1e0de8f559
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 59 additions and 507 deletions

View File

@ -8,13 +8,13 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"slices"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/mlog"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/app" "github.com/mattermost/mattermost/server/v8/channels/app"
"github.com/mattermost/mattermost/server/v8/channels/audit" "github.com/mattermost/mattermost/server/v8/channels/audit"
@ -726,7 +726,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetInvalidParam("role") c.SetInvalidParam("role")
return return
} }
roleValid := pUtils.Contains(roleNamesAll, role) roleValid := slices.Contains(roleNamesAll, role)
if !roleValid { if !roleValid {
c.SetInvalidParam("role") c.SetInvalidParam("role")
return return

View File

@ -6,12 +6,12 @@ package api4
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"slices"
"strconv" "strconv"
"strings" "strings"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/mlog"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/audit" "github.com/mattermost/mattermost/server/v8/channels/audit"
"github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store"
@ -87,7 +87,7 @@ func localGetUsers(c *Context, w http.ResponseWriter, r *http.Request) {
c.SetInvalidParam("role") c.SetInvalidParam("role")
return return
} }
roleValid := pUtils.Contains(roleNamesAll, role) roleValid := slices.Contains(roleNamesAll, role)
if !roleValid { if !roleValid {
c.SetInvalidParam("role") c.SetInvalidParam("role")
return return

View File

@ -9,6 +9,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"slices"
"strings" "strings"
"github.com/mattermost/mattermost/server/v8/channels/utils" "github.com/mattermost/mattermost/server/v8/channels/utils"
@ -18,7 +19,6 @@ import (
"github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/i18n"
"github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/public/shared/request"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store"
"github.com/mattermost/mattermost/server/v8/channels/store/sqlstore" "github.com/mattermost/mattermost/server/v8/channels/store/sqlstore"
) )
@ -1036,7 +1036,7 @@ func (a *App) PatchChannelModerationsForChannel(c request.CTX, channel *model.Ch
for _, channelModerationPatch := range channelModerationsPatch { for _, channelModerationPatch := range channelModerationsPatch {
permissionModified := *channelModerationPatch.Name permissionModified := *channelModerationPatch.Name
if channelModerationPatch.Roles.Guests != nil && pUtils.Contains(model.ChannelModeratedPermissionsChangedByPatch(guestRole, guestRolePatch), permissionModified) { if channelModerationPatch.Roles.Guests != nil && slices.Contains(model.ChannelModeratedPermissionsChangedByPatch(guestRole, guestRolePatch), permissionModified) {
if *channelModerationPatch.Roles.Guests { if *channelModerationPatch.Roles.Guests {
c.Logger().Info("Permission enabled for guests.", mlog.String("permission", permissionModified), mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name)) c.Logger().Info("Permission enabled for guests.", mlog.String("permission", permissionModified), mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name))
} else { } else {
@ -1044,7 +1044,7 @@ func (a *App) PatchChannelModerationsForChannel(c request.CTX, channel *model.Ch
} }
} }
if channelModerationPatch.Roles.Members != nil && pUtils.Contains(model.ChannelModeratedPermissionsChangedByPatch(memberRole, memberRolePatch), permissionModified) { if channelModerationPatch.Roles.Members != nil && slices.Contains(model.ChannelModeratedPermissionsChangedByPatch(memberRole, memberRolePatch), permissionModified) {
if *channelModerationPatch.Roles.Members { if *channelModerationPatch.Roles.Members {
c.Logger().Info("Permission enabled for members.", mlog.String("permission", permissionModified), mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name)) c.Logger().Info("Permission enabled for members.", mlog.String("permission", permissionModified), mlog.String("channel_id", channel.Id), mlog.String("channel_name", channel.Name))
} else { } else {

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"slices"
"testing" "testing"
"time" "time"
@ -17,7 +18,6 @@ import (
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/i18n"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/app/platform" "github.com/mattermost/mattermost/server/v8/channels/app/platform"
"github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store"
@ -56,7 +56,7 @@ func TestSendNotifications(t *testing.T) {
mentions, err := th.App.SendNotifications(th.Context, post1, th.BasicTeam, th.BasicChannel, th.BasicUser, nil, true) mentions, err := th.App.SendNotifications(th.Context, post1, th.BasicTeam, th.BasicChannel, th.BasicUser, nil, true)
require.NoError(t, err) require.NoError(t, err)
require.NotNil(t, mentions) require.NotNil(t, mentions)
require.True(t, pUtils.Contains(mentions, th.BasicUser2.Id), "mentions", mentions) require.True(t, slices.Contains(mentions, th.BasicUser2.Id), "mentions", mentions)
}) })
t.Run("license is required for group mention", func(t *testing.T) { t.Run("license is required for group mention", func(t *testing.T) {
@ -197,7 +197,7 @@ func TestSendNotifications(t *testing.T) {
} }
mentions, err := th.App.SendNotifications(th.Context, childPost, th.BasicTeam, th.BasicChannel, th.BasicUser2, &postList, true) mentions, err := th.App.SendNotifications(th.Context, childPost, th.BasicTeam, th.BasicChannel, th.BasicUser2, &postList, true)
require.NoError(t, err) require.NoError(t, err)
require.False(t, pUtils.Contains(mentions, user.Id)) require.False(t, slices.Contains(mentions, user.Id))
} }
var appErr *model.AppError var appErr *model.AppError
@ -2831,7 +2831,7 @@ func TestReplyPostNotificationsWithCRT(t *testing.T) {
} }
mentions, err := th.App.SendNotifications(th.Context, childPost, th.BasicTeam, th.BasicChannel, th.BasicUser2, &postList, true) mentions, err := th.App.SendNotifications(th.Context, childPost, th.BasicTeam, th.BasicChannel, th.BasicUser2, &postList, true)
require.NoError(t, err) require.NoError(t, err)
assert.False(t, pUtils.Contains(mentions, user.Id)) assert.False(t, slices.Contains(mentions, user.Id))
membership, err := th.App.GetThreadMembershipForUser(user.Id, rootPost.Id) membership, err := th.App.GetThreadMembershipForUser(user.Id, rootPost.Id)
assert.Error(t, err) assert.Error(t, err)

View File

@ -6,12 +6,12 @@ package platform
import ( import (
"context" "context"
"fmt" "fmt"
"slices"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/platform/services/sharedchannel" "github.com/mattermost/mattermost/server/v8/platform/services/sharedchannel"
) )
@ -59,7 +59,7 @@ func (ps *PlatformService) SharedChannelSyncHandler(event *model.WebSocketEvent)
func isEligibleForEvents(syncService SharedChannelServiceIFace, event *model.WebSocketEvent, events []model.WebsocketEventType) bool { func isEligibleForEvents(syncService SharedChannelServiceIFace, event *model.WebSocketEvent, events []model.WebsocketEventType) bool {
return syncServiceEnabled(syncService) && return syncServiceEnabled(syncService) &&
eventHasChannel(event) && eventHasChannel(event) &&
utils.Contains(events, event.EventType()) slices.Contains(events, event.EventType())
} }
func eventHasChannel(event *model.WebSocketEvent) bool { func eventHasChannel(event *model.WebSocketEvent) bool {

View File

@ -11,6 +11,7 @@ import (
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"slices"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -25,7 +26,6 @@ import (
"github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/i18n"
"github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/public/shared/request"
"github.com/mattermost/mattermost/server/public/utils"
) )
const ( const (
@ -877,7 +877,7 @@ func (wc *WebConn) ShouldSendEvent(msg *model.WebSocketEvent) bool {
// For typing events, we don't send them to users who don't have // For typing events, we don't send them to users who don't have
// that channel or thread opened. // that channel or thread opened.
if wc.Platform.Config().FeatureFlags.WebSocketEventScope && if wc.Platform.Config().FeatureFlags.WebSocketEventScope &&
utils.Contains([]model.WebsocketEventType{ slices.Contains([]model.WebsocketEventType{
model.WebsocketEventTyping, model.WebsocketEventTyping,
model.WebsocketEventReactionAdded, model.WebsocketEventReactionAdded,
model.WebsocketEventReactionRemoved, model.WebsocketEventReactionRemoved,

View File

@ -8,6 +8,7 @@ import (
"io" "io"
"net/http" "net/http"
"path/filepath" "path/filepath"
"slices"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/crypto/openpgp" //nolint:staticcheck "golang.org/x/crypto/openpgp" //nolint:staticcheck
@ -15,7 +16,6 @@ import (
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/mattermost/mattermost/server/public/shared/mlog"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/utils" "github.com/mattermost/mattermost/server/v8/channels/utils"
) )
@ -48,7 +48,7 @@ func (a *App) AddPublicKey(name string, key io.Reader) *model.AppError {
} }
a.UpdateConfig(func(cfg *model.Config) { a.UpdateConfig(func(cfg *model.Config) {
if !pUtils.Contains(cfg.PluginSettings.SignaturePublicKeyFiles, name) { if !slices.Contains(cfg.PluginSettings.SignaturePublicKeyFiles, name) {
cfg.PluginSettings.SignaturePublicKeyFiles = append(cfg.PluginSettings.SignaturePublicKeyFiles, name) cfg.PluginSettings.SignaturePublicKeyFiles = append(cfg.PluginSettings.SignaturePublicKeyFiles, name)
} }
}) })

View File

@ -9,10 +9,10 @@ import (
"errors" "errors"
"net/http" "net/http"
"reflect" "reflect"
"slices"
"strings" "strings"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store"
"github.com/mattermost/mattermost/server/v8/channels/utils" "github.com/mattermost/mattermost/server/v8/channels/utils"
@ -189,13 +189,13 @@ func (a *App) UpdateRole(role *model.Role) (*model.Role, *model.AppError) {
builtInRolesMinusChannelRoles := append(utils.RemoveStringsFromSlice(model.BuiltInSchemeManagedRoleIDs, builtInChannelRoles...), model.NewSystemRoleIDs...) builtInRolesMinusChannelRoles := append(utils.RemoveStringsFromSlice(model.BuiltInSchemeManagedRoleIDs, builtInChannelRoles...), model.NewSystemRoleIDs...)
if pUtils.Contains(builtInRolesMinusChannelRoles, savedRole.Name) { if slices.Contains(builtInRolesMinusChannelRoles, savedRole.Name) {
return savedRole, nil return savedRole, nil
} }
var roleRetrievalFunc func() ([]*model.Role, *model.AppError) var roleRetrievalFunc func() ([]*model.Role, *model.AppError)
if pUtils.Contains(builtInChannelRoles, savedRole.Name) { if slices.Contains(builtInChannelRoles, savedRole.Name) {
roleRetrievalFunc = func() ([]*model.Role, *model.AppError) { roleRetrievalFunc = func() ([]*model.Role, *model.AppError) {
roles, nErr := a.Srv().Store().Role().AllChannelSchemeRoles() roles, nErr := a.Srv().Store().Role().AllChannelSchemeRoles()
if nErr != nil { if nErr != nil {

View File

@ -8,6 +8,7 @@ import (
"encoding/csv" "encoding/csv"
"io" "io"
"os" "os"
"slices"
"strconv" "strconv"
"strings" "strings"
"testing" "testing"
@ -15,7 +16,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
pUtils "github.com/mattermost/mattermost/server/public/utils"
) )
type permissionInheritanceTestData struct { type permissionInheritanceTestData struct {
@ -37,7 +37,7 @@ func TestGetRolesByNames(t *testing.T) {
require.NotNil(t, actualRole) require.NotNil(t, actualRole)
require.Equal(t, testData.channelRole.Name, actualRole.Name) require.Equal(t, testData.channelRole.Name, actualRole.Name)
require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id)) require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id))
}) })
} }
@ -47,7 +47,7 @@ func TestGetRoleByName(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, actualRole) require.NotNil(t, actualRole)
require.Equal(t, testData.channelRole.Name, actualRole.Name) require.Equal(t, testData.channelRole.Name, actualRole.Name)
require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow)
}) })
} }
@ -57,7 +57,7 @@ func TestGetRoleByID(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, actualRole) require.NotNil(t, actualRole)
require.Equal(t, testData.channelRole.Id, actualRole.Id) require.Equal(t, testData.channelRole.Id, actualRole.Id)
require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow)
}) })
} }
@ -69,7 +69,7 @@ func TestGetAllRoles(t *testing.T) {
if actualRole.Id == testData.channelRole.Id { if actualRole.Id == testData.channelRole.Id {
require.NotNil(t, actualRole) require.NotNil(t, actualRole)
require.Equal(t, testData.channelRole.Id, actualRole.Id) require.Equal(t, testData.channelRole.Id, actualRole.Id)
require.Equal(t, testData.shouldHavePermission, pUtils.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow) require.Equal(t, testData.shouldHavePermission, slices.Contains(actualRole.Permissions, testData.permission.Id), "row: %+v", testData.truthTableRow)
} }
} }
}) })

View File

@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"slices"
"testing" "testing"
"time" "time"
@ -419,7 +420,7 @@ func TestSessionsLimit(t *testing.T) {
require.Equal(t, maxSessionsLimit, len(gotSessions), "should have maxSessionsLimit number of sessions") require.Equal(t, maxSessionsLimit, len(gotSessions), "should have maxSessionsLimit number of sessions")
// Ensure we are retrieving the same sessions. // Ensure we are retrieving the same sessions.
reverse(gotSessions) slices.Reverse(gotSessions)
for i, sess := range gotSessions { for i, sess := range gotSessions {
require.Equal(t, sessions[i].Id, sess.Id) require.Equal(t, sessions[i].Id, sess.Id)
} }
@ -440,15 +441,8 @@ func TestSessionsLimit(t *testing.T) {
require.Equal(t, maxSessionsLimit, len(gotSessions), "should have maxSessionsLimit number of sessions") require.Equal(t, maxSessionsLimit, len(gotSessions), "should have maxSessionsLimit number of sessions")
// Ensure the the oldest sessions were removed first. // Ensure the the oldest sessions were removed first.
reverse(gotSessions) slices.Reverse(gotSessions)
for i, sess := range gotSessions { for i, sess := range gotSessions {
require.Equal(t, sessions[i].Id, sess.Id) require.Equal(t, sessions[i].Id, sess.Id)
} }
} }
// reverse can be replaced by the slices version when we move to 1.21+
func reverse[S ~[]E, E any](s S) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}

View File

@ -159,11 +159,3 @@ func getBrowserName(ua *uasurfer.UserAgent, userAgentString string) string {
return browserNames[uasurfer.BrowserUnknown] return browserNames[uasurfer.BrowserUnknown]
} }
// min should be replaced by to go 1.21 built-in generic function, see MM-57356.
func min(a, b int) int {
if a < b {
return a
}
return b
}

View File

@ -6,9 +6,9 @@ package app
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"slices"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/app/platform" "github.com/mattermost/mattermost/server/v8/channels/app/platform"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -33,7 +33,7 @@ func (h *addMentionsBroadcastHook) Process(msg *platform.HookedWebSocketEvent, w
return errors.Wrap(err, "Invalid mentions value passed to addMentionsBroadcastHook") return errors.Wrap(err, "Invalid mentions value passed to addMentionsBroadcastHook")
} }
if len(mentions) > 0 && pUtils.Contains[string](mentions, webConn.UserId) { if len(mentions) > 0 && slices.Contains(mentions, webConn.UserId) {
// Note that the client expects this field to be stringified // Note that the client expects this field to be stringified
msg.Add("mentions", model.ArrayToJSON([]string{webConn.UserId})) msg.Add("mentions", model.ArrayToJSON([]string{webConn.UserId}))
} }
@ -55,7 +55,7 @@ func (h *addFollowersBroadcastHook) Process(msg *platform.HookedWebSocketEvent,
return errors.Wrap(err, "Invalid followers value passed to addFollowersBroadcastHook") return errors.Wrap(err, "Invalid followers value passed to addFollowersBroadcastHook")
} }
if len(followers) > 0 && pUtils.Contains[string](followers, webConn.UserId) { if len(followers) > 0 && slices.Contains(followers, webConn.UserId) {
// Note that the client expects this field to be stringified // Note that the client expects this field to be stringified
msg.Add("followers", model.ArrayToJSON([]string{webConn.UserId})) msg.Add("followers", model.ArrayToJSON([]string{webConn.UserId}))
} }

View File

@ -4,10 +4,9 @@
package searchtest package searchtest
import ( import (
"slices"
"testing" "testing"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store"
) )
@ -36,12 +35,12 @@ type searchTest struct {
func filterTestsByTag(tests []searchTest, tags ...string) []searchTest { func filterTestsByTag(tests []searchTest, tags ...string) []searchTest {
filteredTests := []searchTest{} filteredTests := []searchTest{}
for _, test := range tests { for _, test := range tests {
if pUtils.Contains(test.Tags, EngineAll) { if slices.Contains(test.Tags, EngineAll) {
filteredTests = append(filteredTests, test) filteredTests = append(filteredTests, test)
continue continue
} }
for _, tag := range tags { for _, tag := range tags {
if pUtils.Contains(test.Tags, tag) { if slices.Contains(test.Tags, tag) {
filteredTests = append(filteredTests, test) filteredTests = append(filteredTests, test)
break break
} }

View File

@ -4,15 +4,15 @@
package sqlstore package sqlstore
import ( import (
"slices"
"strconv" "strconv"
sq "github.com/mattermost/squirrel" sq "github.com/mattermost/squirrel"
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store"
"github.com/mattermost/mattermost/server/v8/channels/utils" "github.com/mattermost/mattermost/server/v8/channels/utils"
"github.com/pkg/errors"
) )
type SqlChannelBookmarkStore struct { type SqlChannelBookmarkStore struct {
@ -262,7 +262,7 @@ func (s *SqlChannelBookmarkStore) UpdateSortOrder(bookmarkId, channelId string,
} }
bookmarks = utils.RemoveElementFromSliceAtIndex(bookmarks, currentIndex) bookmarks = utils.RemoveElementFromSliceAtIndex(bookmarks, currentIndex)
bookmarks = utils.InsertElementToSliceAtIndex(bookmarks, current, int(newIndex)) bookmarks = slices.Insert(bookmarks, int(newIndex), current)
caseStmt := sq.Case() caseStmt := sq.Case()
query := s.getQueryBuilder(). query := s.getQueryBuilder().
Update("ChannelBookmarks") Update("ChannelBookmarks")

View File

@ -7,6 +7,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"math" "math"
"slices"
"sort" "sort"
"strings" "strings"
"testing" "testing"
@ -17,7 +18,6 @@ import (
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/public/shared/request"
pUtils "github.com/mattermost/mattermost/server/public/utils"
"github.com/mattermost/mattermost/server/v8/channels/store" "github.com/mattermost/mattermost/server/v8/channels/store"
) )
@ -4826,7 +4826,7 @@ func groupTestpUpdateMembersRoleTeam(t *testing.T, rctx request.CTX, ss store.St
require.GreaterOrEqual(t, len(members), 4) // sanity check for team membership require.GreaterOrEqual(t, len(members), 4) // sanity check for team membership
for _, member := range members { for _, member := range members {
if pUtils.Contains(tt.inUserIDs, member.UserId) { if slices.Contains(tt.inUserIDs, member.UserId) {
require.True(t, member.SchemeAdmin) require.True(t, member.SchemeAdmin)
} else { } else {
require.False(t, member.SchemeAdmin) require.False(t, member.SchemeAdmin)
@ -4936,7 +4936,7 @@ func groupTestpUpdateMembersRoleChannel(t *testing.T, rctx request.CTX, ss store
require.GreaterOrEqual(t, len(members), 4) // sanity check for channel membership require.GreaterOrEqual(t, len(members), 4) // sanity check for channel membership
for _, member := range members { for _, member := range members {
if pUtils.Contains(tt.inUserIDs, member.UserId) { if slices.Contains(tt.inUserIDs, member.UserId) {
require.True(t, member.SchemeAdmin) require.True(t, member.SchemeAdmin)
} else { } else {
require.False(t, member.SchemeAdmin) require.False(t, member.SchemeAdmin)

View File

@ -12,11 +12,11 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"path" "path"
"slices"
"strings" "strings"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/shared/i18n" "github.com/mattermost/mattermost/server/public/shared/i18n"
"github.com/mattermost/mattermost/server/public/utils"
) )
func CheckOrigin(r *http.Request, allowedOrigins string) bool { func CheckOrigin(r *http.Request, allowedOrigins string) bool {
@ -114,7 +114,7 @@ func RenderMobileAuthComplete(w http.ResponseWriter, redirectURL string) {
func RenderMobileError(config *model.Config, w http.ResponseWriter, err *model.AppError, redirectURL string) { func RenderMobileError(config *model.Config, w http.ResponseWriter, err *model.AppError, redirectURL string) {
var link = template.HTMLEscapeString(redirectURL) var link = template.HTMLEscapeString(redirectURL)
u, redirectErr := url.Parse(redirectURL) u, redirectErr := url.Parse(redirectURL)
if redirectErr != nil || !utils.Contains(config.NativeAppSettings.AppCustomURLSchemes, u.Scheme) { if redirectErr != nil || !slices.Contains(config.NativeAppSettings.AppCustomURLSchemes, u.Scheme) {
link = *config.ServiceSettings.SiteURL link = *config.ServiceSettings.SiteURL
} }
RenderMobileMessage(w, ` RenderMobileMessage(w, `

View File

@ -9,12 +9,12 @@ import (
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"slices"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/model"
pUtils "github.com/mattermost/mattermost/server/public/utils"
) )
// RemoveStringFromSlice removes the first occurrence of a from slice. // RemoveStringFromSlice removes the first occurrence of a from slice.
@ -32,7 +32,7 @@ func RemoveStringsFromSlice(slice []string, strings ...string) []string {
newSlice := []string{} newSlice := []string{}
for _, item := range slice { for _, item := range slice {
if !pUtils.Contains(strings, item) { if !slices.Contains(strings, item) {
newSlice = append(newSlice, item) newSlice = append(newSlice, item)
} }
} }
@ -87,17 +87,8 @@ func StringSliceDiff(a, b []string) []string {
return result return result
} }
func InsertElementToSliceAtIndex[T comparable](slice []T, element T, index int) []T { func RemoveElementFromSliceAtIndex[S ~[]E, E any](slice S, index int) S {
if len(slice) == index { return slices.Delete(slice, index, index+1)
return append(slice, element)
}
slice = append(slice[:index+1], slice[index:]...)
slice[index] = element
return slice
}
func RemoveElementFromSliceAtIndex[T comparable](slice []T, index int) []T {
return append(slice[:index], slice[index+1:]...)
} }
func GetIPAddress(r *http.Request, trustedProxyIPHeader []string) string { func GetIPAddress(r *http.Request, trustedProxyIPHeader []string) string {
@ -252,24 +243,11 @@ func RoundOffToZeroes(n float64) int64 {
return firstDigit * tens return firstDigit * tens
} }
func MinInt(a, b int) int {
if a < b {
return a
}
return b
}
func MaxInt(a, b int) int {
if a > b {
return a
}
return b
}
// RoundOffToZeroesResolution truncates off at most minResolution zero places. // RoundOffToZeroesResolution truncates off at most minResolution zero places.
// It implicitly sets the lowest minResolution to 0. // It implicitly sets the lowest minResolution to 0.
// e.g. 0 reports 1s, 1 reports 10s, 2 reports 100s, 3 reports 1000s // e.g. 0 reports 1s, 1 reports 10s, 2 reports 100s, 3 reports 1000s
func RoundOffToZeroesResolution(n float64, minResolution int) int64 { func RoundOffToZeroesResolution(n float64, minResolution int) int64 {
resolution := MaxInt(0, minResolution) resolution := max(0, minResolution)
if n >= -9 && n <= 9 { if n >= -9 && n <= 9 {
if resolution == 0 { if resolution == 0 {
return int64(n) return int64(n)
@ -278,7 +256,7 @@ func RoundOffToZeroesResolution(n float64, minResolution int) int64 {
} }
zeroes := int(math.Log10(math.Abs(n))) zeroes := int(math.Log10(math.Abs(n)))
resolution = MinInt(zeroes, resolution) resolution = min(zeroes, resolution)
tens := int64(math.Pow10(resolution)) tens := int64(math.Pow10(resolution))
significantDigits := int64(n) / tens significantDigits := int64(n) / tens
return significantDigits * tens return significantDigits * tens

View File

@ -11,6 +11,7 @@ import (
"math/rand" "math/rand"
"os" "os"
"path/filepath" "path/filepath"
"slices"
"sort" "sort"
"time" "time"
@ -360,7 +361,7 @@ func sampledataCmdF(c client.Client, command *cobra.Command, args []string) erro
totalUsers := 3 + rand.Intn(3) totalUsers := 3 + rand.Intn(3)
for len(users) < totalUsers { for len(users) < totalUsers {
user := allUsers[rand.Intn(len(allUsers))] user := allUsers[rand.Intn(len(allUsers))]
if !pUtils.Contains(users, user) { if !slices.Contains(users, user) {
users = append(users, user) users = append(users, user)
} }
} }
@ -375,7 +376,7 @@ func sampledataCmdF(c client.Client, command *cobra.Command, args []string) erro
totalUsers := 3 + rand.Intn(3) totalUsers := 3 + rand.Intn(3)
for len(users) < totalUsers { for len(users) < totalUsers {
user := allUsers[rand.Intn(len(allUsers))] user := allUsers[rand.Intn(len(allUsers))]
if !pUtils.Contains(users, user) { if !slices.Contains(users, user) {
users = append(users, user) users = append(users, user)
} }
} }

View File

@ -8,11 +8,10 @@ import (
"net/url" "net/url"
"path" "path"
"reflect" "reflect"
"slices"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/utils"
) )
// AutocompleteArgType describes autocomplete argument type // AutocompleteArgType describes autocomplete argument type
@ -235,7 +234,7 @@ func (ad *AutocompleteData) IsValid() error {
return errors.New("Command should be lowercase") return errors.New("Command should be lowercase")
} }
roles := []string{SystemAdminRoleId, SystemUserRoleId, ""} roles := []string{SystemAdminRoleId, SystemUserRoleId, ""}
if !utils.Contains(roles, ad.RoleID) { if !slices.Contains(roles, ad.RoleID) {
return errors.New("Wrong role in the autocomplete data") return errors.New("Wrong role in the autocomplete data")
} }
if len(ad.Arguments) > 0 && len(ad.SubCommands) > 0 { if len(ad.Arguments) > 0 && len(ad.SubCommands) > 0 {

View File

@ -5,10 +5,9 @@ package model
import ( import (
"net/http" "net/http"
"slices"
"strconv" "strconv"
"time" "time"
pUtils "github.com/mattermost/mattermost/server/public/utils"
) )
const ( const (
@ -137,7 +136,7 @@ func (u *UserReportOptions) IsValid() *AppError {
} }
// Validate against the columns we allow sorting for // Validate against the columns we allow sorting for
if !pUtils.Contains(UserReportSortColumns, u.SortColumn) { if !slices.Contains(UserReportSortColumns, u.SortColumn) {
return NewAppError("UserReportOptions.IsValid", "model.user_report_options.is_valid.invalid_sort_column", nil, "", http.StatusBadRequest) return NewAppError("UserReportOptions.IsValid", "model.user_report_options.is_valid.invalid_sort_column", nil, "", http.StatusBadRequest)
} }

View File

@ -6,6 +6,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"maps"
"strconv" "strconv"
) )
@ -272,7 +273,7 @@ func (ev *WebSocketEvent) Copy() *WebSocketEvent {
func (ev *WebSocketEvent) DeepCopy() *WebSocketEvent { func (ev *WebSocketEvent) DeepCopy() *WebSocketEvent {
evCopy := &WebSocketEvent{ evCopy := &WebSocketEvent{
event: ev.event, event: ev.event,
data: copyMap(ev.data), data: maps.Clone(ev.data),
broadcast: ev.broadcast.copy(), broadcast: ev.broadcast.copy(),
sequence: ev.sequence, sequence: ev.sequence,
precomputedJSON: ev.precomputedJSON.copy(), precomputedJSON: ev.precomputedJSON.copy(),
@ -280,14 +281,6 @@ func (ev *WebSocketEvent) DeepCopy() *WebSocketEvent {
return evCopy return evCopy
} }
func copyMap[K comparable, V any](m map[K]V) map[K]V {
dataCopy := make(map[K]V, len(m))
for k, v := range m {
dataCopy[k] = v
}
return dataCopy
}
func (ev *WebSocketEvent) GetData() map[string]any { func (ev *WebSocketEvent) GetData() map[string]any {
return ev.data return ev.data
} }

View File

@ -3,7 +3,7 @@ package pluginapi
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"sort" "slices"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -193,8 +193,7 @@ func (s *MemoryStore) ListKeys(page int, count int, options ...ListKeysOption) (
return []string{}, nil return []string{}, nil
} }
// TODO: Use slices.Sort once the toolchain got updated to go1.21 slices.Sort(allKeys)
sort.Strings(allKeys)
pageKeys := paginateSlice(allKeys, page, count) pageKeys := paginateSlice(allKeys, page, count)

View File

@ -1,14 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package utils
// Contains returns true if the slice contains the item.
func Contains[T comparable](slice []T, item T) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}

View File

@ -1,388 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package utils_test
import (
"testing"
"github.com/mattermost/mattermost/server/public/utils"
)
func TestContains(t *testing.T) {
testCasesStr := []struct {
name string
slice []string
item string
expected bool
}{
{
name: "empty slice",
slice: []string{},
item: "foo",
expected: false,
},
{
name: "slice with item",
slice: []string{"foo"},
item: "foo",
expected: true,
},
{
name: "slice without item",
slice: []string{"bar"},
item: "foo",
expected: false,
},
{
name: "slice with multiple items",
slice: []string{"foo", "bar"},
item: "foo",
expected: true,
},
{
name: "slice with multiple items without item",
slice: []string{"foo", "bar"},
item: "baz",
expected: false,
},
}
for _, tc := range testCasesStr {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
testCasesInt := []struct {
name string
slice []int
item int
expected bool
}{
{
name: "empty slice",
slice: []int{},
item: 1,
expected: false,
},
{
name: "slice with item",
slice: []int{1},
item: 1,
expected: true,
},
{
name: "slice without item",
slice: []int{2},
item: 1,
expected: false,
},
{
name: "slice with multiple items",
slice: []int{1, 2},
item: 1,
expected: true,
},
{
name: "slice with multiple items without item",
slice: []int{1, 2},
item: 3,
expected: false,
},
}
for _, tc := range testCasesInt {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
testCasesFloat := []struct {
name string
slice []float64
item float64
expected bool
}{
{
name: "empty slice",
slice: []float64{},
item: 1.0,
expected: false,
},
{
name: "slice with item",
slice: []float64{1.0},
item: 1.0,
expected: true,
},
{
name: "slice without item",
slice: []float64{2.0},
item: 1.0,
expected: false,
},
{
name: "slice with multiple items",
slice: []float64{1.0, 2.0},
item: 1.0,
expected: true,
},
{
name: "slice with multiple items without item",
slice: []float64{1.0, 2.0},
item: 3.0,
expected: false,
},
}
for _, tc := range testCasesFloat {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
testCasesBool := []struct {
name string
slice []bool
item bool
expected bool
}{
{
name: "empty slice",
slice: []bool{},
item: true,
expected: false,
},
{
name: "slice with item",
slice: []bool{true},
item: true,
expected: true,
},
{
name: "slice without item",
slice: []bool{false},
item: true,
expected: false,
},
{
name: "slice with multiple items",
slice: []bool{true, false},
item: true,
expected: true,
},
{
name: "slice with multiple items without item",
slice: []bool{true, false},
item: false,
expected: true,
},
}
for _, tc := range testCasesBool {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
testCasesByte := []struct {
name string
slice []byte
item byte
expected bool
}{
{
name: "empty slice",
slice: []byte{},
item: 1,
expected: false,
},
{
name: "slice with item",
slice: []byte{1},
item: 1,
expected: true,
},
{
name: "slice without item",
slice: []byte{2},
item: 1,
expected: false,
},
{
name: "slice with multiple items",
slice: []byte{1, 2},
item: 1,
expected: true,
},
{
name: "slice with multiple items without item",
slice: []byte{1, 2},
item: 3,
expected: false,
},
}
for _, tc := range testCasesByte {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
testCasesRune := []struct {
name string
slice []rune
item rune
expected bool
}{
{
name: "empty slice",
slice: []rune{},
item: 1,
expected: false,
},
{
name: "slice with item",
slice: []rune{1},
item: 1,
expected: true,
},
{
name: "slice without item",
slice: []rune{2},
item: 1,
expected: false,
},
{
name: "slice with multiple items",
slice: []rune{1, 2},
item: 1,
expected: true,
},
{
name: "slice with multiple items without item",
slice: []rune{1, 2},
item: 3,
expected: false,
},
}
for _, tc := range testCasesRune {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
testCasesComplex := []struct {
name string
slice []complex128
item complex128
expected bool
}{
{
name: "empty slice",
slice: []complex128{},
item: 1,
expected: false,
},
{
name: "slice with item",
slice: []complex128{1},
item: 1,
expected: true,
},
{
name: "slice without item",
slice: []complex128{2},
item: 1,
expected: false,
},
{
name: "slice with multiple items",
slice: []complex128{1, 2},
item: 1,
expected: true,
},
{
name: "slice with multiple items without item",
slice: []complex128{1, 2},
item: 3,
expected: false,
},
}
for _, tc := range testCasesComplex {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
testCasesUint := []struct {
name string
slice []uint
item uint
expected bool
}{
{
name: "empty slice",
slice: []uint{},
item: 1,
expected: false,
},
{
name: "slice with item",
slice: []uint{1},
item: 1,
expected: true,
},
{
name: "slice without item",
slice: []uint{2},
item: 1,
expected: false,
},
{
name: "slice with multiple items",
slice: []uint{1, 2},
item: 1,
expected: true,
},
{
name: "slice with multiple items without item",
slice: []uint{1, 2},
item: 3,
expected: false,
},
}
for _, tc := range testCasesUint {
t.Run(tc.name, func(t *testing.T) {
actual := utils.Contains(tc.slice, tc.item)
if actual != tc.expected {
t.Errorf("Expected Contains(%v, %v) to be %v, but got %v", tc.slice, tc.item, tc.expected, actual)
}
})
}
}