Merge branch 'master' into mark-as-unread

Этот коммит содержится в:
Harrison Healey
2019-11-04 15:04:28 -05:00
родитель fa34be4aa0 501da809f3
Коммит 47409aaa4b
50 изменённых файлов: 788 добавлений и 420 удалений

Просмотреть файл

@@ -3,10 +3,11 @@ run:
modules-download-mode: vendor modules-download-mode: vendor
linters-settings: linters-settings:
govet:
check-shadowing: true
gofmt: gofmt:
simplify: true simplify: true
govet:
check-shadowing: true
enable-all: true
linters: linters:
disable-all: true disable-all: true

Просмотреть файл

@@ -175,7 +175,7 @@ gofmt: ## Runs gofmt against all packages.
golangci-lint: ## Run golangci-lint on codebasis golangci-lint: ## Run golangci-lint on codebasis
# https://stackoverflow.com/a/677212/1027058 (check if a command exists or not) # https://stackoverflow.com/a/677212/1027058 (check if a command exists or not)
@if ! [ -x "$$(command -v golangci-lintt)" ]; then \ @if ! [ -x "$$(command -v golangci-lint)" ]; then \
echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install for installation instructions."; \ echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install for installation instructions."; \
exit 1; \ exit 1; \
fi; \ fi; \

Просмотреть файл

@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/mattermost/mattermost-server/utils/testutils" "github.com/mattermost/mattermost-server/utils/testutils"
"github.com/stretchr/testify/require"
) )
func TestGetBrandImage(t *testing.T) { func TestGetBrandImage(t *testing.T) {
@@ -32,9 +33,7 @@ func TestUploadBrandImage(t *testing.T) {
Client := th.Client Client := th.Client
data, err := testutils.ReadTestFile("test.png") data, err := testutils.ReadTestFile("test.png")
if err != nil { require.Nil(t, err)
t.Fatal(err)
}
_, resp := Client.UploadBrandImage(data) _, resp := Client.UploadBrandImage(data)
CheckForbiddenStatus(t, resp) CheckForbiddenStatus(t, resp)
@@ -48,7 +47,7 @@ func TestUploadBrandImage(t *testing.T) {
} else if resp.StatusCode == http.StatusUnauthorized { } else if resp.StatusCode == http.StatusUnauthorized {
CheckUnauthorizedStatus(t, resp) CheckUnauthorizedStatus(t, resp)
} else { } else {
t.Fatal("Should have failed either forbidden or unauthorized") require.Fail(t, "Should have failed either forbidden or unauthorized")
} }
_, resp = th.SystemAdminClient.UploadBrandImage(data) _, resp = th.SystemAdminClient.UploadBrandImage(data)
@@ -60,9 +59,7 @@ func TestDeleteBrandImage(t *testing.T) {
defer th.TearDown() defer th.TearDown()
data, err := testutils.ReadTestFile("test.png") data, err := testutils.ReadTestFile("test.png")
if err != nil { require.Nil(t, err)
t.Fatal(err)
}
_, resp := th.SystemAdminClient.UploadBrandImage(data) _, resp := th.SystemAdminClient.UploadBrandImage(data)
CheckCreatedStatus(t, resp) CheckCreatedStatus(t, resp)

Просмотреть файл

@@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/stretchr/testify/require"
) )
func TestGetClusterStatus(t *testing.T) { func TestGetClusterStatus(t *testing.T) {
@@ -22,9 +23,7 @@ func TestGetClusterStatus(t *testing.T) {
infos, resp := th.SystemAdminClient.GetClusterStatus() infos, resp := th.SystemAdminClient.GetClusterStatus()
CheckNoError(t, resp) CheckNoError(t, resp)
if infos == nil { require.NotNil(t, infos, "cluster status should not be nil")
t.Fatal("should not be nil")
}
}) })
t.Run("as restricted system admin", func(t *testing.T) { t.Run("as restricted system admin", func(t *testing.T) {

Просмотреть файл

@@ -108,7 +108,7 @@ func TestGetJobs(t *testing.T) {
received, resp = th.SystemAdminClient.GetJobs(1, 2) received, resp = th.SystemAdminClient.GetJobs(1, 2)
require.Nil(t, resp.Error) require.Nil(t, resp.Error)
require.Equal(t,jobs[1].Id, received[0].Id, "should've received oldest job last") require.Equal(t, jobs[1].Id, received[0].Id, "should've received oldest job last")
_, resp = th.Client.GetJobs(0, 60) _, resp = th.Client.GetJobs(0, 60)
CheckForbiddenStatus(t, resp) CheckForbiddenStatus(t, resp)

Просмотреть файл

@@ -262,14 +262,16 @@ func postLog(c *Context, w http.ResponseWriter, r *http.Request) {
msg = msg[0:399] msg = msg[0:399]
} }
msg = "Client Logs API Endpoint Message: " + msg
fields := []mlog.Field{
mlog.String("type", "client_message"),
mlog.String("user_agent", c.App.UserAgent),
}
if !forceToDebug && lvl == "ERROR" { if !forceToDebug && lvl == "ERROR" {
err := &model.AppError{} mlog.Error(msg, fields...)
err.Message = msg
err.Id = msg
err.Where = "client"
c.LogError(err)
} else { } else {
mlog.Debug("message", mlog.String("message", msg)) mlog.Debug(msg, fields...)
} }
m["message"] = msg m["message"] = msg

Просмотреть файл

@@ -283,6 +283,13 @@ func TestPostLog(t *testing.T) {
_, resp := Client.PostLog(message) _, resp := Client.PostLog(message)
CheckNoError(t, resp) CheckNoError(t, resp)
*th.App.Config().ServiceSettings.EnableDeveloper = false
_, resp = Client.PostLog(message)
CheckNoError(t, resp)
*th.App.Config().ServiceSettings.EnableDeveloper = true
Client.Logout() Client.Logout()
_, resp = Client.PostLog(message) _, resp = Client.PostLog(message)

Просмотреть файл

@@ -925,11 +925,6 @@ func patchUser(c *Context, w http.ResponseWriter, r *http.Request) {
} }
if c.App.Session.IsOAuth && patch.Email != nil { if c.App.Session.IsOAuth && patch.Email != nil {
if err != nil {
c.Err = err
return
}
if ouser.Email != *patch.Email { if ouser.Email != *patch.Email {
c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS) c.SetPermissionError(model.PERMISSION_EDIT_OTHER_USERS)
c.Err.DetailedError += ", attempted email update by oauth app" c.Err.DetailedError += ", attempted email update by oauth app"

Просмотреть файл

@@ -77,10 +77,15 @@ func (a *App) PatchBot(botUserId string, botPatch *model.BotPatch) (*model.Bot,
user.Username = patchedUser.Username user.Username = patchedUser.Username
user.Email = patchedUser.Email user.Email = patchedUser.Email
user.FirstName = patchedUser.FirstName user.FirstName = patchedUser.FirstName
if _, err := a.Srv.Store.User().Update(user, true); err != nil {
userUpdate, err := a.Srv.Store.User().Update(user, true)
if err != nil {
return nil, err return nil, err
} }
ruser := userUpdate.New
a.sendUpdatedUserEvent(*ruser)
return a.Srv.Store.Bot().Update(bot) return a.Srv.Store.Bot().Update(bot)
} }

Просмотреть файл

@@ -172,6 +172,9 @@ func TestPatchBot(t *testing.T) {
patchedBot, err := th.App.PatchBot(createdBot.UserId, botPatch) patchedBot, err := th.App.PatchBot(createdBot.UserId, botPatch)
require.Nil(t, err) require.Nil(t, err)
// patchedBot should create a new .UpdateAt time
require.NotEqual(t, createdBot.UpdateAt, patchedBot.UpdateAt)
createdBot.Username = "username2" createdBot.Username = "username2"
createdBot.DisplayName = "updated bot" createdBot.DisplayName = "updated bot"
createdBot.Description = "an updated bot" createdBot.Description = "an updated bot"

Просмотреть файл

@@ -138,8 +138,10 @@ func (a *App) ensurePostActionCookieSecret() error {
return err return err
} }
system.Value = string(v) system.Value = string(v)
if err = a.Srv.Store.System().Save(system); err == nil { // If we were able to save the key, use it, otherwise log the error.
// If we were able to save the key, use it, otherwise ignore the error. if appErr := a.Srv.Store.System().Save(system); appErr != nil {
mlog.Error("Failed to save PostActionCookieSecret", mlog.Err(appErr))
} else {
secret = newSecret secret = newSecret
} }
} }
@@ -199,8 +201,10 @@ func (a *App) ensureAsymmetricSigningKey() error {
return err return err
} }
system.Value = string(v) system.Value = string(v)
if err = a.Srv.Store.System().Save(system); err == nil { // If we were able to save the key, use it, otherwise log the error.
// If we were able to save the key, use it, otherwise ignore the error. if appErr := a.Srv.Store.System().Save(system); appErr != nil {
mlog.Error("Failed to save AsymmetricSigningKey", mlog.Err(appErr))
} else {
key = newKey key = newKey
} }
} }

Просмотреть файл

@@ -695,6 +695,10 @@ func (api *PluginAPI) InstallPlugin(file io.Reader, replace bool) (*model.Manife
// KV Store Section // KV Store Section
func (api *PluginAPI) KVSetWithOptions(key string, value interface{}, options model.PluginKVSetOptions) (bool, *model.AppError) {
return api.app.SetPluginKeyWithOptions(api.id, key, value, options)
}
func (api *PluginAPI) KVSet(key string, value []byte) *model.AppError { func (api *PluginAPI) KVSet(key string, value []byte) *model.AppError {
return api.app.SetPluginKey(api.id, key, value) return api.app.SetPluginKey(api.id, key, value)
} }

Просмотреть файл

@@ -23,40 +23,30 @@ func (a *App) SetPluginKey(pluginId string, key string, value []byte) *model.App
} }
func (a *App) SetPluginKeyWithExpiry(pluginId string, key string, value []byte, expireInSeconds int64) *model.AppError { func (a *App) SetPluginKeyWithExpiry(pluginId string, key string, value []byte, expireInSeconds int64) *model.AppError {
if expireInSeconds > 0 { options := model.PluginKVSetOptions{
expireInSeconds = model.GetMillis() + (expireInSeconds * 1000) ExpireInSeconds: expireInSeconds,
} }
_, err := a.SetPluginKeyWithOptions(pluginId, key, value, options)
kv := &model.PluginKeyValue{ return err
PluginId: pluginId,
Key: key,
Value: value,
ExpireAt: expireInSeconds,
}
if _, err := a.Srv.Store.Plugin().SaveOrUpdate(kv); err != nil {
mlog.Error("Failed to set plugin key value", mlog.String("plugin_id", pluginId), mlog.String("key", key), mlog.Err(err))
return err
}
// Clean up a previous entry using the hashed key, if it exists.
if err := a.Srv.Store.Plugin().Delete(pluginId, getKeyHash(key)); err != nil {
mlog.Error("Failed to clean up previously hashed plugin key value", mlog.String("plugin_id", pluginId), mlog.String("key", key), mlog.Err(err))
}
return nil
} }
func (a *App) CompareAndSetPluginKey(pluginId string, key string, oldValue, newValue []byte) (bool, *model.AppError) { func (a *App) CompareAndSetPluginKey(pluginId string, key string, oldValue, newValue []byte) (bool, *model.AppError) {
kv := &model.PluginKeyValue{ options := model.PluginKVSetOptions{
PluginId: pluginId, Atomic: true,
Key: key, OldValue: oldValue,
Value: newValue, }
return a.SetPluginKeyWithOptions(pluginId, key, newValue, options)
}
func (a *App) SetPluginKeyWithOptions(pluginId string, key string, value interface{}, options model.PluginKVSetOptions) (bool, *model.AppError) {
if err := options.IsValid(); err != nil {
mlog.Error("Failed to set plugin key value with options", mlog.String("plugin_id", pluginId), mlog.String("key", key), mlog.Err(err))
return false, err
} }
updated, err := a.Srv.Store.Plugin().CompareAndSet(kv, oldValue) updated, err := a.Srv.Store.Plugin().SetWithOptions(pluginId, key, value, options)
if err != nil { if err != nil {
mlog.Error("Failed to compare and set plugin key value", mlog.String("plugin_id", pluginId), mlog.String("key", key), mlog.Err(err)) mlog.Error("Failed to set plugin key value with options", mlog.String("plugin_id", pluginId), mlog.String("key", key), mlog.Err(err))
return updated, err return updated, err
} }

Просмотреть файл

@@ -16,11 +16,12 @@ import (
"testing" "testing"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/plugin" "github.com/mattermost/mattermost-server/plugin"
"github.com/mattermost/mattermost-server/utils/fileutils" "github.com/mattermost/mattermost-server/utils/fileutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func getHashedKey(key string) string { func getHashedKey(key string) string {
@@ -186,6 +187,147 @@ func TestPluginKeyValueStoreCompareAndSet(t *testing.T) {
assert.Equal(t, []byte("test2"), ret) assert.Equal(t, []byte("test2"), ret)
} }
func TestPluginKeyValueStoreSetWithOptionsJSON(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
pluginId := "testpluginid"
defer func() {
assert.Nil(t, th.App.DeletePluginKey(pluginId, "key"))
}()
t.Run("fails with a non-serializable object as the new value", func(t *testing.T) {
result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", func() {}, model.PluginKVSetOptions{
EncodeJSON: true,
})
assert.False(t, result)
assert.NotNil(t, err)
// verify that after the failure it was not set
ret, err := th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(nil), ret)
})
t.Run("fails with a non-serializable object as the old value", func(t *testing.T) {
result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", map[string]interface{}{
"val-a": 10,
}, model.PluginKVSetOptions{
EncodeJSON: true,
Atomic: true,
OldValue: func() {},
})
assert.False(t, result)
assert.NotNil(t, err)
// verify that after the failure it was not set
ret, err := th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(nil), ret)
})
t.Run("storing a value json encoded works", func(t *testing.T) {
result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", map[string]interface{}{
"val-a": 10,
}, model.PluginKVSetOptions{
EncodeJSON: true,
})
assert.True(t, result)
assert.Nil(t, err)
// and I can get it back!
ret, err := th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(`{"val-a":10}`), ret)
})
t.Run("test that setting it atomic when it doesn't match doesn't change anything", func(t *testing.T) {
result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", map[string]interface{}{
"val-a": 30,
}, model.PluginKVSetOptions{
EncodeJSON: true,
Atomic: true,
OldValue: map[string]interface{}{
"val-a": 20,
},
})
assert.False(t, result)
assert.Nil(t, err)
// test that the value didn't change
ret, err := th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(`{"val-a":10}`), ret)
})
t.Run("test the atomic change with the proper old value", func(t *testing.T) {
result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", map[string]interface{}{
"val-a": 30,
}, model.PluginKVSetOptions{
EncodeJSON: true,
Atomic: true,
OldValue: map[string]interface{}{
"val-a": 10,
},
})
assert.True(t, result)
assert.Nil(t, err)
// test that the value did change
ret, err := th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(`{"val-a":30}`), ret)
})
}
func TestPluginKeyValueStoreSetWithOptionsByteArray(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
pluginId := "testpluginid"
defer func() {
assert.Nil(t, th.App.DeletePluginKey(pluginId, "key"))
}()
// storing a value works
result, err := th.App.SetPluginKeyWithOptions(pluginId, "key", []byte(`myvalue`), model.PluginKVSetOptions{})
assert.True(t, result)
assert.Nil(t, err)
// and I can get it back!
ret, err := th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(`myvalue`), ret)
// test that setting it atomic when it doesn't match doesn't change anything
result, err = th.App.SetPluginKeyWithOptions(pluginId, "key", []byte(`newvalue`), model.PluginKVSetOptions{
Atomic: true,
OldValue: []byte(`differentvalue`),
})
assert.False(t, result)
assert.Nil(t, err)
// test that the value didn't change
ret, err = th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(`myvalue`), ret)
// now do the atomic change with the proper old value
result, err = th.App.SetPluginKeyWithOptions(pluginId, "key", []byte(`newvalue`), model.PluginKVSetOptions{
Atomic: true,
OldValue: []byte(`myvalue`),
})
assert.True(t, result)
assert.Nil(t, err)
// test that the value did change
ret, err = th.App.GetPluginKey(pluginId, "key")
assert.Nil(t, err)
assert.Equal(t, []byte(`newvalue`), ret)
}
func TestServePluginRequest(t *testing.T) { func TestServePluginRequest(t *testing.T) {
th := Setup(t).InitBasic() th := Setup(t).InitBasic()
defer th.TearDown() defer th.TearDown()

Просмотреть файл

@@ -72,8 +72,7 @@ func (a *App) GetSession(token string) (*model.Session, *model.AppError) {
return nil, model.NewAppError("GetSession", "api.context.invalid_token.error", map[string]interface{}{"Token": token}, "", http.StatusUnauthorized) return nil, model.NewAppError("GetSession", "api.context.invalid_token.error", map[string]interface{}{"Token": token}, "", http.StatusUnauthorized)
} }
if session != nil && if *a.Config().ServiceSettings.SessionIdleTimeoutInMinutes > 0 &&
*a.Config().ServiceSettings.SessionIdleTimeoutInMinutes > 0 &&
!session.IsOAuth && !session.IsOAuth &&
session.Props[model.SESSION_PROP_TYPE] != model.SESSION_TYPE_USER_ACCESS_TOKEN { session.Props[model.SESSION_PROP_TYPE] != model.SESSION_TYPE_USER_ACCESS_TOKEN {

Просмотреть файл

@@ -11,6 +11,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/mattermost/viper"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@@ -18,7 +19,6 @@ import (
"github.com/mattermost/mattermost-server/mlog" "github.com/mattermost/mattermost-server/mlog"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/utils" "github.com/mattermost/mattermost-server/utils"
"github.com/mattermost/viper"
) )
const noSettingsNamed = "unable to find a setting named: %s" const noSettingsNamed = "unable to find a setting named: %s"
@@ -80,8 +80,17 @@ var MigrateConfigCmd = &cobra.Command{
RunE: configMigrateCmdF, RunE: configMigrateCmdF,
} }
var ConfigResetCmd = &cobra.Command{
Use: "reset",
Short: "Reset config setting",
Long: "Resets the value of a config setting by its name in dot notation or a setting section. Accepts multiple values for array settings.",
Example: "config reset SqlSettings.DriverName LogSettings",
RunE: configResetCmdF,
}
func init() { func init() {
ConfigSubpathCmd.Flags().String("path", "", "Optional subpath; defaults to value in SiteURL") ConfigSubpathCmd.Flags().String("path", "", "Optional subpath; defaults to value in SiteURL")
ConfigResetCmd.Flags().Bool("confirm", false, "Confirm you really want to reset all configuration settings to its default value")
ConfigShowCmd.Flags().Bool("json", false, "Output the configuration as JSON.") ConfigShowCmd.Flags().Bool("json", false, "Output the configuration as JSON.")
ConfigCmd.AddCommand( ConfigCmd.AddCommand(
@@ -91,6 +100,7 @@ func init() {
ConfigShowCmd, ConfigShowCmd,
ConfigSetCmd, ConfigSetCmd,
MigrateConfigCmd, MigrateConfigCmd,
ConfigResetCmd,
) )
RootCmd.AddCommand(ConfigCmd) RootCmd.AddCommand(ConfigCmd)
} }
@@ -367,6 +377,92 @@ func UpdateMap(configMap map[string]interface{}, configSettings []string, newVal
} }
} }
func configResetCmdF(command *cobra.Command, args []string) error {
configStore, err := getConfigStore(command)
if err != nil {
return err
}
defaultConfig := &model.Config{}
defaultConfig.SetDefaults()
confirmFlag, _ := command.Flags().GetBool("confirm")
if confirmFlag {
if _, err = configStore.Set(defaultConfig); err != nil {
return errors.Wrap(err, "failed to set config")
}
}
if !confirmFlag && len(args) == 0 {
var confirmResetAll string
CommandPrettyPrintln("Are you sure you want to reset all the configuration settings?(YES/NO): ")
fmt.Scanln(&confirmResetAll)
if confirmResetAll == "YES" {
if _, err = configStore.Set(defaultConfig); err != nil {
return errors.Wrap(err, "failed to set config")
}
}
}
tempConfig := configStore.Get()
tempConfigMap := configToMap(*tempConfig)
defaultConfigMap := configToMap(*defaultConfig)
for _, arg := range args {
err = changeMap(tempConfigMap, defaultConfigMap, strings.Split(arg, "."))
if err != nil {
return errors.Wrap(err, "Failed to reset config")
}
}
bs, err := json.Marshal(tempConfigMap)
if err != nil {
fmt.Printf("Error while marshalling map to json %s\n", err)
os.Exit(1)
}
err = json.Unmarshal(bs, tempConfig)
if err != nil {
fmt.Printf("Error while unmarshalling json to struct %s\n", err)
os.Exit(1)
}
if changed := config.FixInvalidLocales(tempConfig); changed {
return errors.New("Invalid locale configuration")
}
if _, err := configStore.Set(tempConfig); err != nil {
return errors.Wrap(err, "failed to set config")
}
return nil
}
func changeMap(oldConfigMap, defaultConfigMap map[string]interface{}, configSettings []string) error {
resOld, ok := oldConfigMap[configSettings[0]]
if !ok {
return fmt.Errorf("Unable to find a setting with that name %s", configSettings[0])
}
resDef := defaultConfigMap[configSettings[0]]
valueOld := reflect.ValueOf(resOld)
if valueOld.Kind() == reflect.Map {
if len(configSettings) == 1 {
return changeSection(resOld.(map[string]interface{}), resDef.(map[string]interface{}))
}
return changeMap(resOld.(map[string]interface{}), resDef.(map[string]interface{}), configSettings[1:])
}
if len(configSettings) == 1 {
oldConfigMap[configSettings[0]] = defaultConfigMap[configSettings[0]]
return nil
}
return fmt.Errorf("Unable to find a setting with that name %s", configSettings[0])
}
func changeSection(oldConfigMap, defaultConfigMap map[string]interface{}) error {
valueOld := reflect.ValueOf(oldConfigMap)
for _, key := range valueOld.MapKeys() {
oldConfigMap[key.String()] = defaultConfigMap[key.String()]
}
return nil
}
// configToMap converts our config into a map // configToMap converts our config into a map
func configToMap(s interface{}) map[string]interface{} { func configToMap(s interface{}) map[string]interface{} {
return structToMap(s) return structToMap(s)

Просмотреть файл

@@ -13,11 +13,10 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/mattermost/mattermost-server/config"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/config"
"github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/model"
) )
@@ -168,6 +167,64 @@ func TestConfigSet(t *testing.T) {
}) })
} }
func TestConfigReset(t *testing.T) {
th := Setup()
defer th.TearDown()
t.Run("No Error when no arguments are given (reset all the configurations)", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "reset"))
})
t.Run("No Error when a configuration section is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings"))
})
t.Run("No Error when a configuration setting is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings.RunJobs"))
})
t.Run("Error when the wrong configuration section is given", func(t *testing.T) {
assert.Error(t, th.RunCommand(t, "config", "reset", "InvalidSettings"))
})
t.Run("Error when the wrong configuration setting is given", func(t *testing.T) {
assert.Error(t, th.RunCommand(t, "config", "reset", "JobSettings.InvalidConfiguration"))
})
t.Run("Success when the confirm boolean flag is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "PrivacySettings.ShowFullName", "false"))
assert.NoError(t, th.RunCommand(t, "config", "reset", "--confirm"))
output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
output2 := th.CheckCommand(t, "config", "get", "PrivacySettings.ShowFullName")
assert.Contains(t, output1, "true")
assert.Contains(t, output2, "true")
})
t.Run("Success when a configuration section is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunScheduler", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "PrivacySettings.ShowFullName", "false"))
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings"))
output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
output2 := th.CheckCommand(t, "config", "get", "JobSettings.RunScheduler")
output3 := th.CheckCommand(t, "config", "get", "PrivacySettings.ShowFullName")
assert.Contains(t, output1, "true")
assert.Contains(t, output2, "true")
assert.Contains(t, output3, "false")
})
t.Run("Success when a configuration setting is given", func(t *testing.T) {
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunScheduler", "false"))
assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings.RunJobs"))
output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
output2 := th.CheckCommand(t, "config", "get", "JobSettings.RunScheduler")
assert.Contains(t, output1, "true")
assert.Contains(t, output2, "false")
})
}
func TestConfigToMap(t *testing.T) { func TestConfigToMap(t *testing.T) {
// This test is almost the same as TestStructToMap, but I have it here for the sake of completions // This test is almost the same as TestStructToMap, but I have it here for the sake of completions
cases := []struct { cases := []struct {

Просмотреть файл

@@ -15,7 +15,7 @@ import (
_ "github.com/mattermost/mattermost-server/imports" _ "github.com/mattermost/mattermost-server/imports"
// Enterprise Deps // Enterprise Deps
_ "github.com/dgryski/dgoogauth" _ "github.com/gorilla/handlers"
_ "github.com/hako/durafmt" _ "github.com/hako/durafmt"
_ "github.com/hashicorp/memberlist" _ "github.com/hashicorp/memberlist"
_ "github.com/mattermost/ldap" _ "github.com/mattermost/ldap"

Просмотреть файл

@@ -21,8 +21,3 @@ func UnmarshalConfig(r io.Reader, allowEnvironmentOverrides bool) (*model.Config
func InitializeConfigurationsTable(db *sqlx.DB) error { func InitializeConfigurationsTable(db *sqlx.DB) error {
return initializeConfigurationsTable(db) return initializeConfigurationsTable(db)
} }
// ResolveConfigFilePath exposes the internal resolveConfigFilePath to test only.
func ResolveConfigFilePath(path string) (string, error) {
return resolveConfigFilePath(path)
}

2
go.mod
Просмотреть файл

@@ -52,8 +52,6 @@ require (
github.com/miekg/dns v1.1.19 // indirect github.com/miekg/dns v1.1.19 // indirect
github.com/minio/minio-go/v6 v6.0.38 github.com/minio/minio-go/v6 v6.0.38
github.com/mitchellh/go-testing-interface v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/muesli/smartcrop v0.3.0 // indirect github.com/muesli/smartcrop v0.3.0 // indirect
github.com/olekukonko/tablewriter v0.0.1 // indirect github.com/olekukonko/tablewriter v0.0.1 // indirect
github.com/onsi/ginkgo v1.8.0 // indirect github.com/onsi/ginkgo v1.8.0 // indirect

Просмотреть файл

@@ -4524,15 +4524,15 @@
}, },
{ {
"id": "model.config.is_valid.elastic_search.connection_url.app_error", "id": "model.config.is_valid.elastic_search.connection_url.app_error",
"translation": "Elasticsearch ConnectionUrl setting must be provided when Elastic Search indexing is enabled." "translation": "Elasticsearch ConnectionUrl setting must be provided when Elasticsearch indexing is enabled."
}, },
{ {
"id": "model.config.is_valid.elastic_search.enable_autocomplete.app_error", "id": "model.config.is_valid.elastic_search.enable_autocomplete.app_error",
"translation": "Elasticsearch IndexingEnabled setting must be set to true when Elastic Search AutocompleteEnabled is set to true." "translation": "Elasticsearch IndexingEnabled setting must be set to true when Elasticsearch AutocompleteEnabled is set to true."
}, },
{ {
"id": "model.config.is_valid.elastic_search.enable_searching.app_error", "id": "model.config.is_valid.elastic_search.enable_searching.app_error",
"translation": "Elasticsearch IndexingEnabled setting must be set to true when Elastic Search SearchEnabled is set to true." "translation": "Elasticsearch IndexingEnabled setting must be set to true when Elasticsearch SearchEnabled is set to true."
}, },
{ {
"id": "model.config.is_valid.elastic_search.live_indexing_batch_size.app_error", "id": "model.config.is_valid.elastic_search.live_indexing_batch_size.app_error",
@@ -5154,6 +5154,14 @@
"id": "model.plugin_key_value.is_valid.plugin_id.app_error", "id": "model.plugin_key_value.is_valid.plugin_id.app_error",
"translation": "Invalid plugin ID, must be more than {{.Min}} and a of maximum {{.Max}} characters long." "translation": "Invalid plugin ID, must be more than {{.Min}} and a of maximum {{.Max}} characters long."
}, },
{
"id": "model.plugin_kvset_options.is_valid.old_value.app_error",
"translation": "Invalid old value, it shouldn't be set when the operation is not atomic."
},
{
"id": "model.plugin_kvset_options.serialize_value.app_error",
"translation": "Could not deserialize a value. EncodeJSON: {{.EncodeJSON}}."
},
{ {
"id": "model.post.is_valid.channel_id.app_error", "id": "model.post.is_valid.channel_id.app_error",
"translation": "Invalid channel id" "translation": "Invalid channel id"
@@ -7488,11 +7496,11 @@
}, },
{ {
"id": "web.error.unsupported_browser.no_longer_support", "id": "web.error.unsupported_browser.no_longer_support",
"translation": "We no longer support this browser" "translation": "This browser is no longer supported by Mattermost"
}, },
{ {
"id": "web.error.unsupported_browser.no_longer_support_version", "id": "web.error.unsupported_browser.no_longer_support_version",
"translation": "We no longer support this version of your browser" "translation": "This version of your browser is no longer supported by Mattermost"
}, },
{ {
"id": "web.error.unsupported_browser.open_system_browser.edge", "id": "web.error.unsupported_browser.open_system_browser.edge",

Просмотреть файл

@@ -21,6 +21,7 @@ type MessageExport struct {
PostId *string PostId *string
PostCreateAt *int64 PostCreateAt *int64
PostUpdateAt *int64 PostUpdateAt *int64
PostDeleteAt *int64
PostMessage *string PostMessage *string
PostType *string PostType *string
PostRootId *string PostRootId *string

77
model/plugin_kvset_options.go Обычный файл
Просмотреть файл

@@ -0,0 +1,77 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package model
import (
"encoding/json"
"net/http"
)
// PluginKVSetOptions contains information on how to store a value in the plugin KV store.
type PluginKVSetOptions struct {
EncodeJSON bool // If true, store the JSON encoding of newValue
Atomic bool // Only store the value if the current value matches the oldValue
OldValue interface{} // The value to compare with the current value. Only used when Atomic is true
ExpireInSeconds int64 // Set an expire counter
}
// IsValid returns nil if the chosen options are valid.
func (opt *PluginKVSetOptions) IsValid() *AppError {
if !opt.Atomic && opt.OldValue != nil {
return NewAppError(
"PluginKVSetOptions.IsValid",
"model.plugin_kvset_options.is_valid.old_value.app_error",
nil,
"",
http.StatusBadRequest,
)
}
return nil
}
// GetOldValueSerialized returns the serialized old value either as directly
// or encoded as JSON depending on the chosen options.
func (opt *PluginKVSetOptions) GetOldValueSerialized() ([]byte, *AppError) {
return opt.serializeValue(opt.OldValue)
}
func (opt *PluginKVSetOptions) serializeValue(value interface{}) ([]byte, *AppError) {
if opt.EncodeJSON {
data, err := json.Marshal(value)
if err != nil {
return nil, NewAppError("PluginKVSetOptions.serializeValue", "model.plugin_kvset_options.serialize_value.app_error", map[string]interface{}{"EncodeJSON": opt.EncodeJSON}, "Could not serialize JSON value", http.StatusBadRequest)
}
return data, nil
}
castResult, ok := value.([]byte)
if !ok {
return nil, NewAppError("PluginKVSetOptions.SerializeValue", "model.plugin_kvset_options.serialize_value.app_error", map[string]interface{}{"EncodeJSON": opt.EncodeJSON}, "Could not cast value to []byte", http.StatusBadRequest)
}
return castResult, nil
}
// NewPluginKeyValueFromOptions return a PluginKeyValue given a pluginID, a KV pair and options.
func NewPluginKeyValueFromOptions(pluginId, key string, value interface{}, opt PluginKVSetOptions) (*PluginKeyValue, *AppError) {
serializedValue, err := opt.serializeValue(value)
if err != nil {
return nil, err
}
expireAt := int64(0)
if opt.ExpireInSeconds > 0 {
expireAt = GetMillis() + (opt.ExpireInSeconds * 1000)
}
kv := &PluginKeyValue{
PluginId: pluginId,
Key: key,
Value: serializedValue,
ExpireAt: expireAt,
}
return kv, nil
}

Просмотреть файл

@@ -612,6 +612,15 @@ type API interface {
// Minimum server version: 5.16 // Minimum server version: 5.16
KVCompareAndDelete(key string, oldValue []byte) (bool, *model.AppError) KVCompareAndDelete(key string, oldValue []byte) (bool, *model.AppError)
// KVSetWithOptions stores a key-value pair, unique per plugin, according to the given options.
// If options.EncodeJSON is not true, the type of newValue must be of type []byte.
// Returns (false, err) if DB error occurred
// Returns (false, nil) if the value was not set
// Returns (true, nil) if the value was set
//
// Minimum server version: 5.18
KVSetWithOptions(key string, newValue interface{}, options model.PluginKVSetOptions) (bool, *model.AppError)
// KVSet stores a key-value pair with an expiry time, unique per plugin. // KVSet stores a key-value pair with an expiry time, unique per plugin.
// //
// Minimum server version: 5.6 // Minimum server version: 5.6

Просмотреть файл

@@ -286,8 +286,8 @@ func (g *hooksRPCClient) ServeHTTP(c *Context, w http.ResponseWriter, r *http.Re
defer connection.Close() defer connection.Close()
rpcServer := rpc.NewServer() rpcServer := rpc.NewServer()
if err := rpcServer.RegisterName("Plugin", &httpResponseWriterRPCServer{w: w}); err != nil { if err := rpcServer.RegisterName("Plugin", &httpResponseWriterRPCServer{w: w, log: g.log}); err != nil {
g.log.Error("Plugin failed to ServeHTTP, coulden't register RPC name", mlog.Err(err)) g.log.Error("Plugin failed to ServeHTTP, couldn't register RPC name", mlog.Err(err))
return return
} }
rpcServer.ServeConn(connection) rpcServer.ServeConn(connection)

Просмотреть файл

@@ -3727,6 +3727,37 @@ func (s *apiRPCServer) KVCompareAndDelete(args *Z_KVCompareAndDeleteArgs, return
return nil return nil
} }
type Z_KVSetWithOptionsArgs struct {
A string
B interface{}
C model.PluginKVSetOptions
}
type Z_KVSetWithOptionsReturns struct {
A bool
B *model.AppError
}
func (g *apiRPCClient) KVSetWithOptions(key string, newValue interface{}, options model.PluginKVSetOptions) (bool, *model.AppError) {
_args := &Z_KVSetWithOptionsArgs{key, newValue, options}
_returns := &Z_KVSetWithOptionsReturns{}
if err := g.client.Call("Plugin.KVSetWithOptions", _args, _returns); err != nil {
log.Printf("RPC call to KVSetWithOptions API failed: %s", err.Error())
}
return _returns.A, _returns.B
}
func (s *apiRPCServer) KVSetWithOptions(args *Z_KVSetWithOptionsArgs, returns *Z_KVSetWithOptionsReturns) error {
if hook, ok := s.impl.(interface {
KVSetWithOptions(key string, newValue interface{}, options model.PluginKVSetOptions) (bool, *model.AppError)
}); ok {
returns.A, returns.B = hook.KVSetWithOptions(args.A, args.B, args.C)
} else {
return encodableError(fmt.Errorf("API KVSetWithOptions called but not implemented."))
}
return nil
}
type Z_KVSetWithExpiryArgs struct { type Z_KVSetWithExpiryArgs struct {
A string A string
B []byte B []byte

Просмотреть файл

@@ -14,6 +14,8 @@ type Helpers interface {
// KVSetJSON stores a key-value pair, unique per plugin, marshalling the given value as a JSON string. // KVSetJSON stores a key-value pair, unique per plugin, marshalling the given value as a JSON string.
// //
// Deprecated: Use p.API.KVSetWithOptions instead.
//
// Minimum server version: 5.2 // Minimum server version: 5.2
KVSetJSON(key string, value interface{}) error KVSetJSON(key string, value interface{}) error
@@ -23,6 +25,8 @@ type Helpers interface {
// Returns (false, nil) if current value != oldValue or key already exists when inserting // Returns (false, nil) if current value != oldValue or key already exists when inserting
// Returns (true, nil) if current value == oldValue or new key is inserted // Returns (true, nil) if current value == oldValue or new key is inserted
// //
// Deprecated: Use p.API.KVSetWithOptions instead.
//
// Minimum server version: 5.12 // Minimum server version: 5.12
KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error)
@@ -41,6 +45,8 @@ type Helpers interface {
// KVSetWithExpiryJSON stores a key-value pair with an expiry time, unique per plugin, marshalling the given value as a JSON string. // KVSetWithExpiryJSON stores a key-value pair with an expiry time, unique per plugin, marshalling the given value as a JSON string.
// //
// Deprecated: Use p.API.KVSetWithOptions instead.
//
// Minimum server version: 5.6 // Minimum server version: 5.6
KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error
} }

Просмотреть файл

@@ -9,25 +9,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// KVGetJSON is a wrapper around KVGet to simplify reading a JSON object from the key value store. // KVSetJSON implements Helpers.KVSetJSON.
func (p *HelpersImpl) KVGetJSON(key string, value interface{}) (bool, error) {
data, appErr := p.API.KVGet(key)
if appErr != nil {
return false, appErr
}
if data == nil {
return false, nil
}
err := json.Unmarshal(data, value)
if err != nil {
return false, err
}
return true, nil
}
// KVSetJSON is a wrapper around KVSet to simplify writing a JSON object to the key value store.
func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error { func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error {
data, err := json.Marshal(value) data, err := json.Marshal(value)
if err != nil { if err != nil {
@@ -42,7 +24,7 @@ func (p *HelpersImpl) KVSetJSON(key string, value interface{}) error {
return nil return nil
} }
// KVCompareAndSetJSON is a wrapper around KVCompareAndSet to simplify atomically writing a JSON object to the key value store. // KVCompareAndSetJSON implements Helpers.KVCompareAndSetJSON.
func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) { func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newValue interface{}) (bool, error) {
var oldData, newData []byte var oldData, newData []byte
var err error var err error
@@ -69,7 +51,7 @@ func (p *HelpersImpl) KVCompareAndSetJSON(key string, oldValue interface{}, newV
return set, nil return set, nil
} }
// KVCompareAndDeleteJSON is a wrapper around KVCompareAndDelete to simplify atomically deleting a JSON object from the key value store. // KVCompareAndDeleteJSON implements Helpers.KVCompareAndDeleteJSON.
func (p *HelpersImpl) KVCompareAndDeleteJSON(key string, oldValue interface{}) (bool, error) { func (p *HelpersImpl) KVCompareAndDeleteJSON(key string, oldValue interface{}) (bool, error) {
var oldData []byte var oldData []byte
var err error var err error
@@ -89,7 +71,25 @@ func (p *HelpersImpl) KVCompareAndDeleteJSON(key string, oldValue interface{}) (
return deleted, nil return deleted, nil
} }
// KVSetWithExpiryJSON is a wrapper around KVSetWithExpiry to simplify atomically writing a JSON object with expiry to the key value store. // KVGetJSON implements Helpers.KVGetJSON.
func (p *HelpersImpl) KVGetJSON(key string, value interface{}) (bool, error) {
data, appErr := p.API.KVGet(key)
if appErr != nil {
return false, appErr
}
if data == nil {
return false, nil
}
err := json.Unmarshal(data, value)
if err != nil {
return false, err
}
return true, nil
}
// KVSetWithExpiryJSON implements Helpers.KVSetWithExpiryJSON.
func (p *HelpersImpl) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error { func (p *HelpersImpl) KVSetWithExpiryJSON(key string, value interface{}, expireInSeconds int64) error {
data, err := json.Marshal(value) data, err := json.Marshal(value)
if err != nil { if err != nil {

Просмотреть файл

@@ -4,13 +4,18 @@
package plugin package plugin
import ( import (
"errors"
"fmt"
"io" "io"
"net/http" "net/http"
"net/rpc" "net/rpc"
"github.com/mattermost/mattermost-server/mlog"
) )
type httpResponseWriterRPCServer struct { type httpResponseWriterRPCServer struct {
w http.ResponseWriter w http.ResponseWriter
log *mlog.Logger
} }
func (w *httpResponseWriterRPCServer) Header(args struct{}, reply *http.Header) error { func (w *httpResponseWriterRPCServer) Header(args struct{}, reply *http.Header) error {
@@ -24,6 +29,12 @@ func (w *httpResponseWriterRPCServer) Write(args []byte, reply *struct{}) error
} }
func (w *httpResponseWriterRPCServer) WriteHeader(args int, reply *struct{}) error { func (w *httpResponseWriterRPCServer) WriteHeader(args int, reply *struct{}) error {
// Check if args is a valid http status code. This prevents plugins from crashing the server with a panic.
// This is a copy of the checkWriteHeaderCode function in net/http/server.go in the go source.
if args < 100 || args > 999 {
w.log.Error(fmt.Sprintf("Plugin tried to write an invalid http status code: %v. Did not write the invalid header.", args))
return errors.New("invalid http status code")
}
w.w.WriteHeader(args) w.w.WriteHeader(args)
return nil return nil
} }

Просмотреть файл

@@ -2023,6 +2023,31 @@ func (_m *API) HasPermissionToTeam(userId string, teamId string, permission *mod
return r0 return r0
} }
// InstallPlugin provides a mock function with given fields: file, replace
func (_m *API) InstallPlugin(file io.Reader, replace bool) (*model.Manifest, *model.AppError) {
ret := _m.Called(file, replace)
var r0 *model.Manifest
if rf, ok := ret.Get(0).(func(io.Reader, bool) *model.Manifest); ok {
r0 = rf(file, replace)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Manifest)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(io.Reader, bool) *model.AppError); ok {
r1 = rf(file, replace)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// KVCompareAndDelete provides a mock function with given fields: key, oldValue // KVCompareAndDelete provides a mock function with given fields: key, oldValue
func (_m *API) KVCompareAndDelete(key string, oldValue []byte) (bool, *model.AppError) { func (_m *API) KVCompareAndDelete(key string, oldValue []byte) (bool, *model.AppError) {
ret := _m.Called(key, oldValue) ret := _m.Called(key, oldValue)
@@ -2183,6 +2208,29 @@ func (_m *API) KVSetWithExpiry(key string, value []byte, expireInSeconds int64)
return r0 return r0
} }
// KVSetWithOptions provides a mock function with given fields: key, newValue, options
func (_m *API) KVSetWithOptions(key string, newValue interface{}, options model.PluginKVSetOptions) (bool, *model.AppError) {
ret := _m.Called(key, newValue, options)
var r0 bool
if rf, ok := ret.Get(0).(func(string, interface{}, model.PluginKVSetOptions) bool); ok {
r0 = rf(key, newValue, options)
} else {
r0 = ret.Get(0).(bool)
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, interface{}, model.PluginKVSetOptions) *model.AppError); ok {
r1 = rf(key, newValue, options)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// LoadPluginConfiguration provides a mock function with given fields: dest // LoadPluginConfiguration provides a mock function with given fields: dest
func (_m *API) LoadPluginConfiguration(dest interface{}) error { func (_m *API) LoadPluginConfiguration(dest interface{}) error {
ret := _m.Called(dest) ret := _m.Called(dest)
@@ -2885,28 +2933,3 @@ func (_m *API) UploadFile(data []byte, channelId string, filename string) (*mode
return r0, r1 return r0, r1
} }
// InstallPlugin provides a mock function with given fields: file, replace
func (_m *API) InstallPlugin(file io.Reader, replace bool) (*model.Manifest, *model.AppError) {
ret := _m.Called(file, replace)
var r0 *model.Manifest
if rf, ok := ret.Get(0).(func(io.Reader, bool) *model.Manifest); ok {
r0 = rf(file, replace)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Manifest)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(io.Reader, bool) *model.AppError); ok {
r1 = rf(file, replace)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}

Просмотреть файл

@@ -8,7 +8,7 @@ import (
) )
func stringify(objects []interface{}) []string { func stringify(objects []interface{}) []string {
stringified := make([]string, len(objects), len(objects)) stringified := make([]string, len(objects))
for i, object := range objects { for i, object := range objects {
stringified[i] = fmt.Sprintf("%+v", object) stringified[i] = fmt.Sprintf("%+v", object)
} }

Просмотреть файл

@@ -15,7 +15,7 @@ func TestStringify(t *testing.T) {
assert.Empty(t, strings) assert.Empty(t, strings)
}) })
t.Run("EmptyShouldReturnEmpty", func(t *testing.T) { t.Run("EmptyShouldReturnEmpty", func(t *testing.T) {
strings := stringify(make([]interface{}, 0, 0)) strings := stringify(make([]interface{}, 0))
assert.Empty(t, strings) assert.Empty(t, strings)
}) })
t.Run("PrimitivesAndCompositesShouldReturnCorrectValues", func(t *testing.T) { t.Run("PrimitivesAndCompositesShouldReturnCorrectValues", func(t *testing.T) {
@@ -83,7 +83,7 @@ func TestToObjects(t *testing.T) {
assert.Nil(t, objects) assert.Nil(t, objects)
}) })
t.Run("EmptyShouldReturnEmpty", func(t *testing.T) { t.Run("EmptyShouldReturnEmpty", func(t *testing.T) {
objects := toObjects(make([]string, 0, 0)) objects := toObjects(make([]string, 0))
assert.Empty(t, objects) assert.Empty(t, objects)
}) })
t.Run("ShouldReturnSliceOfObjects", func(t *testing.T) { t.Run("ShouldReturnSliceOfObjects", func(t *testing.T) {

Просмотреть файл

@@ -9,12 +9,3 @@ const (
LSH_NO_CACHE LayeredStoreHint = iota LSH_NO_CACHE LayeredStoreHint = iota
LSH_MASTER_ONLY LSH_MASTER_ONLY
) )
func hintsContains(hints []LayeredStoreHint, contains LayeredStoreHint) bool {
for _, hint := range hints {
if hint == contains {
return true
}
}
return false
}

Просмотреть файл

@@ -49,30 +49,6 @@ func (s *LocalCacheSupplier) Next() LayeredStoreSupplier {
return s.next return s.next
} }
func (s *LocalCacheSupplier) doStandardReadCache(ctx context.Context, cache ObjectCache, key string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
if hintsContains(hints, LSH_NO_CACHE) {
if s.metrics != nil {
s.metrics.IncrementMemCacheMissCounter(cache.Name())
}
return nil
}
if cacheItem, ok := cache.Get(key); ok {
if s.metrics != nil {
s.metrics.IncrementMemCacheHitCounter(cache.Name())
}
result := NewSupplierResult()
result.Data = cacheItem
return result
}
if s.metrics != nil {
s.metrics.IncrementMemCacheMissCounter(cache.Name())
}
return nil
}
func (s *LocalCacheSupplier) doStandardAddToCache(ctx context.Context, cache ObjectCache, key string, result *LayeredStoreSupplierResult, hints ...LayeredStoreHint) { func (s *LocalCacheSupplier) doStandardAddToCache(ctx context.Context, cache ObjectCache, key string, result *LayeredStoreSupplierResult, hints ...LayeredStoreHint) {
if result.Err == nil && result.Data != nil { if result.Err == nil && result.Data != nil {
cache.AddWithDefaultExpires(key, result.Data) cache.AddWithDefaultExpires(key, result.Data)

Просмотреть файл

@@ -35,19 +35,6 @@ func StoreTest(t *testing.T, f func(*testing.T, store.Store)) {
} }
} }
func StoreTestWithSqlSupplier(t *testing.T, f func(*testing.T, store.Store, storetest.SqlSupplier)) {
defer func() {
if err := recover(); err != nil {
tearDownStores()
panic(err)
}
}()
for _, st := range storeTypes {
st := st
t.Run(st.Name, func(t *testing.T) { f(t, st.Store, st.SqlSupplier) })
}
}
func initStores() { func initStores() {
storeTypes = append(storeTypes, &storeType{ storeTypes = append(storeTypes, &storeType{
Name: "LocalCache+MySQL", Name: "LocalCache+MySQL",

Просмотреть файл

@@ -85,9 +85,9 @@ func (s SqlAuditStore) PermanentDeleteBatch(endTime int64, limit int64) (int64,
return 0, model.NewAppError("SqlAuditStore.PermanentDeleteBatch", "store.sql_audit.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlAuditStore.PermanentDeleteBatch", "store.sql_audit.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
rowsAffected, err1 := sqlResult.RowsAffected() rowsAffected, err := sqlResult.RowsAffected()
if err1 != nil { if err != nil {
return 0, model.NewAppError("SqlAuditStore.PermanentDeleteBatch", "store.sql_audit.permanent_delete_batch.app_error", nil, ""+err1.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlAuditStore.PermanentDeleteBatch", "store.sql_audit.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
return rowsAffected, nil return rowsAffected, nil
} }

Просмотреть файл

@@ -75,7 +75,7 @@ func (us SqlComplianceStore) Get(id string) (*model.Compliance, *model.AppError)
return nil, model.NewAppError("SqlComplianceStore.Get", "store.sql_compliance.get.finding.app_error", nil, err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("SqlComplianceStore.Get", "store.sql_compliance.get.finding.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
if obj == nil { if obj == nil {
return nil, model.NewAppError("SqlComplianceStore.Get", "store.sql_compliance.get.finding.app_error", nil, err.Error(), http.StatusNotFound) return nil, model.NewAppError("SqlComplianceStore.Get", "store.sql_compliance.get.finding.app_error", nil, "", http.StatusNotFound)
} }
return obj.(*model.Compliance), nil return obj.(*model.Compliance), nil
} }
@@ -213,6 +213,7 @@ func (s SqlComplianceStore) MessageExport(after int64, limit int) ([]*model.Mess
Posts.Id AS PostId, Posts.Id AS PostId,
Posts.CreateAt AS PostCreateAt, Posts.CreateAt AS PostCreateAt,
Posts.UpdateAt AS PostUpdateAt, Posts.UpdateAt AS PostUpdateAt,
Posts.DeleteAt AS PostDeleteAt,
Posts.Message AS PostMessage, Posts.Message AS PostMessage,
Posts.Type AS PostType, Posts.Type AS PostType,
Posts.OriginalId AS PostOriginalId, Posts.OriginalId AS PostOriginalId,
@@ -241,7 +242,7 @@ func (s SqlComplianceStore) MessageExport(after int64, limit int) ([]*model.Mess
LEFT OUTER JOIN Users ON Posts.UserId = Users.Id LEFT OUTER JOIN Users ON Posts.UserId = Users.Id
LEFT JOIN Bots ON Bots.UserId = Posts.UserId LEFT JOIN Bots ON Bots.UserId = Posts.UserId
WHERE WHERE
(Posts.CreateAt > :StartTime OR Posts.EditAt > :StartTime) AND (Posts.CreateAt > :StartTime OR Posts.EditAt > :StartTime OR Posts.DeleteAt > :StartTime) AND
Posts.Type = '' Posts.Type = ''
ORDER BY PostUpdateAt ORDER BY PostUpdateAt
LIMIT :Limit` LIMIT :Limit`

Просмотреть файл

@@ -136,7 +136,7 @@ func (es SqlEmojiStore) Delete(emoji *model.Emoji, time int64) *model.AppError {
AND DeleteAt = 0`, map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": emoji.Id}); err != nil { AND DeleteAt = 0`, map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": emoji.Id}); err != nil {
return model.NewAppError("SqlEmojiStore.Delete", "store.sql_emoji.delete.app_error", nil, "id="+emoji.Id+", err="+err.Error(), http.StatusInternalServerError) return model.NewAppError("SqlEmojiStore.Delete", "store.sql_emoji.delete.app_error", nil, "id="+emoji.Id+", err="+err.Error(), http.StatusInternalServerError)
} else if rows, _ := sqlResult.RowsAffected(); rows == 0 { } else if rows, _ := sqlResult.RowsAffected(); rows == 0 {
return model.NewAppError("SqlEmojiStore.Delete", "store.sql_emoji.delete.no_results", nil, "id="+emoji.Id+", err="+err.Error(), http.StatusBadRequest) return model.NewAppError("SqlEmojiStore.Delete", "store.sql_emoji.delete.no_results", nil, "id="+emoji.Id, http.StatusBadRequest)
} }
es.removeFromCache(emoji) es.removeFromCache(emoji)

Просмотреть файл

@@ -266,10 +266,12 @@ func (s SqlFileInfoStore) PermanentDeleteBatch(endTime int64, limit int64) (int6
if err != nil { if err != nil {
return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
rowsAffected, err1 := sqlResult.RowsAffected()
if err1 != nil { rowsAffected, err := sqlResult.RowsAffected()
if err != nil {
return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteBatch", "store.sql_file_info.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
return rowsAffected, nil return rowsAffected, nil
} }
@@ -281,9 +283,10 @@ func (s SqlFileInfoStore) PermanentDeleteByUser(userId string) (int64, *model.Ap
return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
rowsAffected, err1 := sqlResult.RowsAffected() rowsAffected, err := sqlResult.RowsAffected()
if err1 != nil { if err != nil {
return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlFileInfoStore.PermanentDeleteByUser", "store.sql_file_info.PermanentDeleteByUser.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
return rowsAffected, nil return rowsAffected, nil
} }

Просмотреть файл

@@ -586,7 +586,7 @@ func (s *SqlGroupStore) UpdateGroupSyncable(groupSyncable *model.GroupSyncable)
case model.GroupSyncableTypeChannel: case model.GroupSyncableTypeChannel:
_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable)) _, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
default: default:
return nil, model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId+", "+err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("SqlGroupStore.GroupUpdateGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId, http.StatusInternalServerError)
} }
if err != nil { if err != nil {
@@ -619,7 +619,7 @@ func (s *SqlGroupStore) DeleteGroupSyncable(groupID string, syncableID string, s
case model.GroupSyncableTypeChannel: case model.GroupSyncableTypeChannel:
_, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable)) _, err = s.GetMaster().Update(groupSyncableToGroupChannel(groupSyncable))
default: default:
return nil, model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId+", "+err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("SqlGroupStore.GroupDeleteGroupSyncable", "model.group_syncable.type.app_error", nil, "group_id="+groupSyncable.GroupId+", syncable_id="+groupSyncable.SyncableId, http.StatusInternalServerError)
} }
if err != nil { if err != nil {

Просмотреть файл

@@ -91,12 +91,13 @@ func (ps SqlPluginStore) CompareAndSet(kv *model.PluginKeyValue, oldValue []byte
} else { } else {
// Update if oldValue is not nil // Update if oldValue is not nil
updateResult, err := ps.GetMaster().Exec( updateResult, err := ps.GetMaster().Exec(
`UPDATE PluginKeyValueStore SET PValue = :New WHERE PluginId = :PluginId AND PKey = :Key AND PValue = :Old`, `UPDATE PluginKeyValueStore SET PValue = :New, ExpireAt = :ExpireAt WHERE PluginId = :PluginId AND PKey = :Key AND PValue = :Old`,
map[string]interface{}{ map[string]interface{}{
"PluginId": kv.PluginId, "PluginId": kv.PluginId,
"Key": kv.Key, "Key": kv.Key,
"Old": oldValue, "Old": oldValue,
"New": kv.Value, "New": kv.Value,
"ExpireAt": kv.ExpireAt,
}, },
) )
if err != nil { if err != nil {
@@ -147,6 +148,34 @@ func (ps SqlPluginStore) CompareAndDelete(kv *model.PluginKeyValue, oldValue []b
return true, nil return true, nil
} }
func (ps SqlPluginStore) SetWithOptions(pluginId string, key string, value interface{}, opt model.PluginKVSetOptions) (bool, *model.AppError) {
if err := opt.IsValid(); err != nil {
return false, err
}
kv, err := model.NewPluginKeyValueFromOptions(pluginId, key, value, opt)
if err != nil {
return false, err
}
if opt.Atomic {
var serializedOldValue []byte
serializedOldValue, err = opt.GetOldValueSerialized()
if err != nil {
return false, err
}
return ps.CompareAndSet(kv, serializedOldValue)
}
savedKv, err := ps.SaveOrUpdate(kv)
if err != nil {
return false, err
}
return savedKv != nil, nil
}
func (ps SqlPluginStore) Get(pluginId, key string) (*model.PluginKeyValue, *model.AppError) { func (ps SqlPluginStore) Get(pluginId, key string) (*model.PluginKeyValue, *model.AppError) {
var kv *model.PluginKeyValue var kv *model.PluginKeyValue
currentTime := model.GetMillis() currentTime := model.GetMillis()

Просмотреть файл

@@ -160,8 +160,8 @@ func (s *SqlReactionStore) PermanentDeleteBatch(endTime int64, limit int64) (int
return 0, model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
rowsAffected, err1 := sqlResult.RowsAffected() rowsAffected, err := sqlResult.RowsAffected()
if err1 != nil { if err != nil {
return 0, model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError) return 0, model.NewAppError("SqlReactionStore.PermanentDeleteBatch", "store.sql_reaction.permanent_delete_batch.app_error", nil, ""+err.Error(), http.StatusInternalServerError)
} }
return rowsAffected, nil return rowsAffected, nil

Просмотреть файл

@@ -1448,10 +1448,9 @@ func (us SqlUserStore) GetUsersBatchForIndexing(startTime, endTime int64, limit
OrderBy("u.CreateAt"). OrderBy("u.CreateAt").
Limit(uint64(limit)). Limit(uint64(limit)).
ToSql() ToSql()
_, err1 := us.GetSearchReplica().Select(&users, usersQuery, args...) _, err := us.GetSearchReplica().Select(&users, usersQuery, args...)
if err != nil {
if err1 != nil { return nil, model.NewAppError("SqlUserStore.GetUsersBatchForIndexing", "store.sql_user.get_users_batch_for_indexing.get_users.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("SqlUserStore.GetUsersBatchForIndexing", "store.sql_user.get_users_batch_for_indexing.get_users.app_error", nil, err1.Error(), http.StatusInternalServerError)
} }
userIds := []string{} userIds := []string{}
@@ -1478,10 +1477,9 @@ func (us SqlUserStore) GetUsersBatchForIndexing(startTime, endTime int64, limit
Join("Channels c ON cm.ChannelId = c.Id"). Join("Channels c ON cm.ChannelId = c.Id").
Where(sq.Eq{"c.Type": "O", "cm.UserId": userIds}). Where(sq.Eq{"c.Type": "O", "cm.UserId": userIds}).
ToSql() ToSql()
_, err2 := us.GetSearchReplica().Select(&channelMembers, channelMembersQuery, args...) _, err = us.GetSearchReplica().Select(&channelMembers, channelMembersQuery, args...)
if err != nil {
if err2 != nil { return nil, model.NewAppError("SqlUserStore.GetUsersBatchForIndexing", "store.sql_user.get_users_batch_for_indexing.get_channel_members.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("SqlUserStore.GetUsersBatchForIndexing", "store.sql_user.get_users_batch_for_indexing.get_channel_members.app_error", nil, err2.Error(), http.StatusInternalServerError)
} }
var teamMembers []*model.TeamMember var teamMembers []*model.TeamMember
@@ -1490,10 +1488,9 @@ func (us SqlUserStore) GetUsersBatchForIndexing(startTime, endTime int64, limit
From("TeamMembers"). From("TeamMembers").
Where(sq.Eq{"UserId": userIds, "DeleteAt": 0}). Where(sq.Eq{"UserId": userIds, "DeleteAt": 0}).
ToSql() ToSql()
_, err3 := us.GetSearchReplica().Select(&teamMembers, teamMembersQuery, args...) _, err = us.GetSearchReplica().Select(&teamMembers, teamMembersQuery, args...)
if err != nil {
if err3 != nil { return nil, model.NewAppError("SqlUserStore.GetUsersBatchForIndexing", "store.sql_user.get_users_batch_for_indexing.get_team_members.app_error", nil, err.Error(), http.StatusInternalServerError)
return nil, model.NewAppError("SqlUserStore.GetUsersBatchForIndexing", "store.sql_user.get_users_batch_for_indexing.get_team_members.app_error", nil, err3.Error(), http.StatusInternalServerError)
} }
userMap := map[string]*model.UserForIndexing{} userMap := map[string]*model.UserForIndexing{}

Просмотреть файл

@@ -533,6 +533,7 @@ type PluginStore interface {
SaveOrUpdate(keyVal *model.PluginKeyValue) (*model.PluginKeyValue, *model.AppError) SaveOrUpdate(keyVal *model.PluginKeyValue) (*model.PluginKeyValue, *model.AppError)
CompareAndSet(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) CompareAndSet(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError)
CompareAndDelete(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError) CompareAndDelete(keyVal *model.PluginKeyValue, oldValue []byte) (bool, *model.AppError)
SetWithOptions(pluginId string, key string, value interface{}, options model.PluginKVSetOptions) (bool, *model.AppError)
Get(pluginId, key string) (*model.PluginKeyValue, *model.AppError) Get(pluginId, key string) (*model.PluginKeyValue, *model.AppError)
Delete(pluginId, key string) *model.AppError Delete(pluginId, key string) *model.AppError
DeleteAllForPlugin(PluginId string) *model.AppError DeleteAllForPlugin(PluginId string) *model.AppError

Просмотреть файл

@@ -182,3 +182,26 @@ func (_m *PluginStore) SaveOrUpdate(keyVal *model.PluginKeyValue) (*model.Plugin
return r0, r1 return r0, r1
} }
// SetWithOptions provides a mock function with given fields: pluginId, key, value, options
func (_m *PluginStore) SetWithOptions(pluginId string, key string, value interface{}, options model.PluginKVSetOptions) (bool, *model.AppError) {
ret := _m.Called(pluginId, key, value, options)
var r0 bool
if rf, ok := ret.Get(0).(func(string, string, interface{}, model.PluginKVSetOptions) bool); ok {
r0 = rf(pluginId, key, value, options)
} else {
r0 = ret.Get(0).(bool)
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(string, string, interface{}, model.PluginKVSetOptions) *model.AppError); ok {
r1 = rf(pluginId, key, value, options)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}

Просмотреть файл

@@ -23,9 +23,8 @@ func testStatusStore(t *testing.T, ss store.Store) {
status.LastActivityAt = 10 status.LastActivityAt = 10
if _, err := ss.Status().Get(status.UserId); err != nil { _, err := ss.Status().Get(status.UserId)
t.Fatal(err) require.Nil(t, err)
}
status2 := &model.Status{UserId: model.NewId(), Status: model.STATUS_AWAY, Manual: false, LastActivityAt: 0, ActiveChannel: ""} status2 := &model.Status{UserId: model.NewId(), Status: model.STATUS_AWAY, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
require.Nil(t, ss.Status().SaveOrUpdate(status2)) require.Nil(t, ss.Status().SaveOrUpdate(status2))
@@ -33,40 +32,28 @@ func testStatusStore(t *testing.T, ss store.Store) {
status3 := &model.Status{UserId: model.NewId(), Status: model.STATUS_OFFLINE, Manual: false, LastActivityAt: 0, ActiveChannel: ""} status3 := &model.Status{UserId: model.NewId(), Status: model.STATUS_OFFLINE, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
require.Nil(t, ss.Status().SaveOrUpdate(status3)) require.Nil(t, ss.Status().SaveOrUpdate(status3))
if statuses, err := ss.Status().GetByIds([]string{status.UserId, "junk"}); err != nil { statuses, err := ss.Status().GetByIds([]string{status.UserId, "junk"})
t.Fatal(err) require.Nil(t, err)
} else { require.Len(t, statuses, 1, "should only have 1 status")
if len(statuses) != 1 {
t.Fatal("should only have 1 status")
}
}
if err := ss.Status().ResetAll(); err != nil { err = ss.Status().ResetAll()
t.Fatal(err) require.Nil(t, err)
}
if statusParameter, err := ss.Status().Get(status.UserId); err != nil { statusParameter, err := ss.Status().Get(status.UserId)
t.Fatal(err) require.Nil(t, err)
} else { require.Equal(t, statusParameter.Status, model.STATUS_OFFLINE, "should be offline")
if statusParameter.Status != model.STATUS_OFFLINE {
t.Fatal("should be offline")
}
}
if err := ss.Status().UpdateLastActivityAt(status.UserId, 10); err != nil { err = ss.Status().UpdateLastActivityAt(status.UserId, 10)
t.Fatal(err) require.Nil(t, err)
}
} }
func testActiveUserCount(t *testing.T, ss store.Store) { func testActiveUserCount(t *testing.T, ss store.Store) {
status := &model.Status{UserId: model.NewId(), Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} status := &model.Status{UserId: model.NewId(), Status: model.STATUS_ONLINE, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
require.Nil(t, ss.Status().SaveOrUpdate(status)) require.Nil(t, ss.Status().SaveOrUpdate(status))
if count, err := ss.Status().GetTotalActiveUsersCount(); err != nil { count, err := ss.Status().GetTotalActiveUsersCount()
t.Fatal(err) require.Nil(t, err)
} else { require.True(t, count > 0, "expected count > 0, got %d", count)
require.True(t, count > 0, "expected count > 0, got %d", count)
}
} }
type ByUserId []*model.Status type ByUserId []*model.Status

Просмотреть файл

@@ -92,62 +92,57 @@ func testUserStoreSave(t *testing.T, ss store.Store) {
Username: model.NewId(), Username: model.NewId(),
} }
if _, err := ss.User().Save(&u1); err != nil { _, err := ss.User().Save(&u1)
t.Fatal("couldn't save user", err) require.Nil(t, err, "couldn't save user")
}
defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }()
_, err := ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}, maxUsersPerTeam) _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}, maxUsersPerTeam)
require.Nil(t, err) require.Nil(t, err)
if _, err := ss.User().Save(&u1); err == nil { _, err = ss.User().Save(&u1)
t.Fatal("shouldn't be able to update user from save") require.NotNil(t, err, "shouldn't be able to update user from save")
}
u2 := model.User{ u2 := model.User{
Email: u1.Email, Email: u1.Email,
Username: model.NewId(), Username: model.NewId(),
} }
if _, err := ss.User().Save(&u2); err == nil { _, err = ss.User().Save(&u2)
t.Fatal("should be unique email") require.NotNil(t, err, "should be unique email")
}
u2.Email = MakeEmail() u2.Email = MakeEmail()
u2.Username = u1.Username u2.Username = u1.Username
if _, err := ss.User().Save(&u1); err == nil { _, err = ss.User().Save(&u1)
t.Fatal("should be unique username") require.NotNil(t, err, "should be unique username")
}
u2.Username = "" u2.Username = ""
if _, err := ss.User().Save(&u1); err == nil { _, err = ss.User().Save(&u1)
t.Fatal("should be unique username") require.NotNil(t, err, "should be unique username")
}
for i := 0; i < 49; i++ { for i := 0; i < 49; i++ {
u := model.User{ u := model.User{
Email: MakeEmail(), Email: MakeEmail(),
Username: model.NewId(), Username: model.NewId(),
} }
if _, err := ss.User().Save(&u); err != nil { _, err = ss.User().Save(&u)
t.Fatal("couldn't save item", err) require.Nil(t, err, "couldn't save item")
}
defer func() { require.Nil(t, ss.User().PermanentDelete(u.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u.Id)) }()
_, err := ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u.Id}, maxUsersPerTeam) _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u.Id}, maxUsersPerTeam)
require.Nil(t, err) require.Nil(t, err)
} }
u2.Id = "" u2.Id = ""
u2.Email = MakeEmail() u2.Email = MakeEmail()
u2.Username = model.NewId() u2.Username = model.NewId()
if _, err := ss.User().Save(&u2); err != nil { _, err = ss.User().Save(&u2)
t.Fatal("couldn't save item", err) require.Nil(t, err, "couldn't save item")
}
defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }()
if _, err := ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}, maxUsersPerTeam); err == nil { _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}, maxUsersPerTeam)
t.Fatal("should be the limit") require.NotNil(t, err, "should be the limit")
}
} }
func testUserStoreUpdate(t *testing.T, ss store.Store) { func testUserStoreUpdate(t *testing.T, ss store.Store) {
@@ -170,26 +165,22 @@ func testUserStoreUpdate(t *testing.T, ss store.Store) {
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id}, -1) _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u2.Id}, -1)
require.Nil(t, err) require.Nil(t, err)
if _, err = ss.User().Update(u1, false); err != nil { _, err = ss.User().Update(u1, false)
t.Fatal(err) require.Nil(t, err)
}
missing := &model.User{} missing := &model.User{}
if _, err = ss.User().Update(missing, false); err == nil { _, err = ss.User().Update(missing, false)
t.Fatal("Update should have failed because of missing key") require.NotNil(t, err, "Update should have failed because of missing key")
}
newId := &model.User{ newId := &model.User{
Id: model.NewId(), Id: model.NewId(),
} }
if _, err = ss.User().Update(newId, false); err == nil { _, err = ss.User().Update(newId, false)
t.Fatal("Update should have failed because id change") require.NotNil(t, err, "Update should have failed because id change")
}
u2.Email = MakeEmail() u2.Email = MakeEmail()
if _, err = ss.User().Update(u2, false); err == nil { _, err = ss.User().Update(u2, false)
t.Fatal("Update should have failed because you can't modify AD/LDAP fields") require.NotNil(t, err, "Update should have failed because you can't modify AD/LDAP fields")
}
u3 := &model.User{ u3 := &model.User{
Email: MakeEmail(), Email: MakeEmail(),
@@ -203,28 +194,17 @@ func testUserStoreUpdate(t *testing.T, ss store.Store) {
require.Nil(t, err) require.Nil(t, err)
u3.Email = MakeEmail() u3.Email = MakeEmail()
if userUpdate, err := ss.User().Update(u3, false); err != nil { userUpdate, err := ss.User().Update(u3, false)
t.Fatal("Update should not have failed") require.Nil(t, err, "Update should not have failed")
} else { assert.Equal(t, oldEmail, userUpdate.New.Email, "Email should not have been updated as the update is not trusted")
newUser := userUpdate.New
if newUser.Email != oldEmail {
t.Fatal("Email should not have been updated as the update is not trusted")
}
}
u3.Email = MakeEmail() u3.Email = MakeEmail()
if userUpdate, err := ss.User().Update(u3, true); err != nil { userUpdate, err = ss.User().Update(u3, true)
t.Fatal("Update should not have failed") require.Nil(t, err, "Update should not have failed")
} else { assert.NotEqual(t, oldEmail, userUpdate.New.Email, "Email should have been updated as the update is trusted")
newUser := userUpdate.New
if newUser.Email == oldEmail {
t.Fatal("Email should have been updated as the update is trusted")
}
}
if err := ss.User().UpdateLastPictureUpdate(u1.Id); err != nil { err = ss.User().UpdateLastPictureUpdate(u1.Id)
t.Fatal("Update should not have failed") require.Nil(t, err, "Update should not have failed")
}
} }
func testUserStoreUpdateUpdateAt(t *testing.T, ss store.Store) { func testUserStoreUpdateUpdateAt(t *testing.T, ss store.Store) {
@@ -236,16 +216,12 @@ func testUserStoreUpdateUpdateAt(t *testing.T, ss store.Store) {
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1) _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1)
require.Nil(t, err) require.Nil(t, err)
if _, err = ss.User().UpdateUpdateAt(u1.Id); err != nil { _, err = ss.User().UpdateUpdateAt(u1.Id)
t.Fatal(err) require.Nil(t, err)
}
user, err := ss.User().Get(u1.Id) user, err := ss.User().Get(u1.Id)
require.Nil(t, err) require.Nil(t, err)
if user.UpdateAt <= u1.UpdateAt { require.Less(t, u1.UpdateAt, user.UpdateAt, "UpdateAt not updated correctly")
t.Fatal("UpdateAt not updated correctly")
}
} }
func testUserStoreUpdateFailedPasswordAttempts(t *testing.T, ss store.Store) { func testUserStoreUpdateFailedPasswordAttempts(t *testing.T, ss store.Store) {
@@ -257,15 +233,12 @@ func testUserStoreUpdateFailedPasswordAttempts(t *testing.T, ss store.Store) {
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1) _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1)
require.Nil(t, err) require.Nil(t, err)
if err = ss.User().UpdateFailedPasswordAttempts(u1.Id, 3); err != nil { err = ss.User().UpdateFailedPasswordAttempts(u1.Id, 3)
t.Fatal(err) require.Nil(t, err)
}
user, err := ss.User().Get(u1.Id) user, err := ss.User().Get(u1.Id)
require.Nil(t, err) require.Nil(t, err)
if user.FailedAttempts != 3 { require.Equal(t, 3, user.FailedAttempts, "FailedAttempts not updated correctly")
t.Fatal("FailedAttempts not updated correctly")
}
} }
func testUserStoreGet(t *testing.T, ss store.Store) { func testUserStoreGet(t *testing.T, ss store.Store) {
@@ -1448,7 +1421,7 @@ func testUserStoreGetProfileByGroupChannelIdsForUser(t *testing.T, ss store.Stor
users, ok := res[channelId] users, ok := res[channelId]
require.True(t, ok) require.True(t, ok)
userIds := []string{} var userIds []string
for _, user := range users { for _, user := range users {
userIds = append(userIds, user.Id) userIds = append(userIds, user.Id)
} }
@@ -1901,17 +1874,12 @@ func testUserStoreUpdatePassword(t *testing.T, ss store.Store) {
hashedPassword := model.HashPassword("newpwd") hashedPassword := model.HashPassword("newpwd")
if err := ss.User().UpdatePassword(u1.Id, hashedPassword); err != nil { err = ss.User().UpdatePassword(u1.Id, hashedPassword)
t.Fatal(err) require.Nil(t, err)
}
if user, err := ss.User().GetByEmail(u1.Email); err != nil { user, err := ss.User().GetByEmail(u1.Email)
t.Fatal(err) require.Nil(t, err)
} else { require.Equal(t, user.Password, hashedPassword, "Password was not updated correctly")
if user.Password != hashedPassword {
t.Fatal("Password was not updated correctly")
}
}
} }
func testUserStoreDelete(t *testing.T, ss store.Store) { func testUserStoreDelete(t *testing.T, ss store.Store) {
@@ -1923,9 +1891,8 @@ func testUserStoreDelete(t *testing.T, ss store.Store) {
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1) _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: model.NewId(), UserId: u1.Id}, -1)
require.Nil(t, err) require.Nil(t, err)
if err := ss.User().PermanentDelete(u1.Id); err != nil { err = ss.User().PermanentDelete(u1.Id)
t.Fatal(err) require.Nil(t, err)
}
} }
func testUserStoreUpdateAuthData(t *testing.T, ss store.Store) { func testUserStoreUpdateAuthData(t *testing.T, ss store.Store) {
@@ -1945,19 +1912,11 @@ func testUserStoreUpdateAuthData(t *testing.T, ss store.Store) {
_, err = ss.User().UpdateAuthData(u1.Id, service, &authData, "", true) _, err = ss.User().UpdateAuthData(u1.Id, service, &authData, "", true)
require.Nil(t, err) require.Nil(t, err)
if user, err := ss.User().GetByEmail(u1.Email); err != nil { user, err := ss.User().GetByEmail(u1.Email)
t.Fatal(err) require.Nil(t, err)
} else { require.Equal(t, service, user.AuthService, "AuthService was not updated correctly")
if user.AuthService != service { require.Equal(t, authData, *user.AuthData, "AuthData was not updated correctly")
t.Fatal("AuthService was not updated correctly") require.Equal(t, "", user.Password, "Password was not cleared properly")
}
if *user.AuthData != authData {
t.Fatal("AuthData was not updated correctly")
}
if user.Password != "" {
t.Fatal("Password was not cleared properly")
}
}
} }
func testUserUnreadCount(t *testing.T, ss store.Store) { func testUserUnreadCount(t *testing.T, ss store.Store) {
@@ -1993,9 +1952,8 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id}, -1) _, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u2.Id}, -1)
require.Nil(t, err) require.Nil(t, err)
if _, channelErr := ss.Channel().Save(&c1, -1); err != nil { _, err = ss.Channel().Save(&c1, -1)
t.Fatal("couldn't save item", channelErr) require.Nil(t, err, "couldn't save item")
}
m1 := model.ChannelMember{} m1 := model.ChannelMember{}
m1.ChannelId = c1.Id m1.ChannelId = c1.Id
@@ -2013,9 +1971,8 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
m1.ChannelId = c2.Id m1.ChannelId = c2.Id
m2.ChannelId = c2.Id m2.ChannelId = c2.Id
if _, err = ss.Channel().SaveDirectChannel(&c2, &m1, &m2); err != nil { _, err = ss.Channel().SaveDirectChannel(&c2, &m1, &m2)
t.Fatal("couldn't save direct channel", err) require.Nil(t, err, "couldn't save direct channel")
}
p1 := model.Post{} p1 := model.Post{}
p1.ChannelId = c1.Id p1.ChannelId = c1.Id
@@ -2051,21 +2008,15 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
badge, unreadCountErr := ss.User().GetUnreadCount(u2.Id) badge, unreadCountErr := ss.User().GetUnreadCount(u2.Id)
require.Nil(t, unreadCountErr) require.Nil(t, unreadCountErr)
if badge != 3 { require.Equal(t, int64(3), badge, "should have 3 unread messages")
t.Fatal("should have 3 unread messages")
}
badge, unreadCountErr = ss.User().GetUnreadCountForChannel(u2.Id, c1.Id) badge, unreadCountErr = ss.User().GetUnreadCountForChannel(u2.Id, c1.Id)
require.Nil(t, unreadCountErr) require.Nil(t, unreadCountErr)
if badge != 1 { require.Equal(t, int64(1), badge, "should have 1 unread messages for that channel")
t.Fatal("should have 1 unread messages for that channel")
}
badge, unreadCountErr = ss.User().GetUnreadCountForChannel(u2.Id, c2.Id) badge, unreadCountErr = ss.User().GetUnreadCountForChannel(u2.Id, c2.Id)
require.Nil(t, unreadCountErr) require.Nil(t, unreadCountErr)
if badge != 2 { require.Equal(t, int64(2), badge, "should have 2 unread messages for that channel")
t.Fatal("should have 2 unread messages for that channel")
}
} }
func testUserStoreUpdateMfaSecret(t *testing.T, ss store.Store) { func testUserStoreUpdateMfaSecret(t *testing.T, ss store.Store) {
@@ -2075,14 +2026,12 @@ func testUserStoreUpdateMfaSecret(t *testing.T, ss store.Store) {
require.Nil(t, err) require.Nil(t, err)
defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }()
if err = ss.User().UpdateMfaSecret(u1.Id, "12345"); err != nil { err = ss.User().UpdateMfaSecret(u1.Id, "12345")
t.Fatal(err) require.Nil(t, err)
}
// should pass, no update will occur though // should pass, no update will occur though
if err = ss.User().UpdateMfaSecret("junk", "12345"); err != nil { err = ss.User().UpdateMfaSecret("junk", "12345")
t.Fatal(err) require.Nil(t, err)
}
} }
func testUserStoreUpdateMfaActive(t *testing.T, ss store.Store) { func testUserStoreUpdateMfaActive(t *testing.T, ss store.Store) {
@@ -2094,18 +2043,15 @@ func testUserStoreUpdateMfaActive(t *testing.T, ss store.Store) {
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
if err = ss.User().UpdateMfaActive(u1.Id, true); err != nil { err = ss.User().UpdateMfaActive(u1.Id, true)
t.Fatal(err) require.Nil(t, err)
}
if err = ss.User().UpdateMfaActive(u1.Id, false); err != nil { err = ss.User().UpdateMfaActive(u1.Id, false)
t.Fatal(err) require.Nil(t, err)
}
// should pass, no update will occur though // should pass, no update will occur though
if err = ss.User().UpdateMfaActive("junk", true); err != nil { err = ss.User().UpdateMfaActive("junk", true)
t.Fatal(err) require.Nil(t, err)
}
} }
func testUserStoreGetRecentlyActiveUsersForTeam(t *testing.T, ss store.Store, s SqlSupplier) { func testUserStoreGetRecentlyActiveUsersForTeam(t *testing.T, ss store.Store, s SqlSupplier) {
@@ -3516,9 +3462,7 @@ func testUserStoreAnalyticsGetInactiveUsersCount(t *testing.T, ss store.Store) {
defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }()
count, err := ss.User().AnalyticsGetInactiveUsersCount() count, err := ss.User().AnalyticsGetInactiveUsersCount()
if err != nil { require.Nil(t, err)
t.Fatal(err)
}
u2 := &model.User{} u2 := &model.User{}
u2.Email = MakeEmail() u2.Email = MakeEmail()
@@ -3528,22 +3472,13 @@ func testUserStoreAnalyticsGetInactiveUsersCount(t *testing.T, ss store.Store) {
defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }()
newCount, err := ss.User().AnalyticsGetInactiveUsersCount() newCount, err := ss.User().AnalyticsGetInactiveUsersCount()
if err != nil { require.Nil(t, err)
t.Fatal(err) require.Equal(t, count, newCount-1, "Expected 1 more inactive users but found otherwise.")
}
if count != newCount-1 {
t.Fatal("Expected 1 more inactive users but found otherwise.", count, newCount)
}
} }
func testUserStoreAnalyticsGetSystemAdminCount(t *testing.T, ss store.Store) { func testUserStoreAnalyticsGetSystemAdminCount(t *testing.T, ss store.Store) {
var countBefore int64 countBefore, err := ss.User().AnalyticsGetSystemAdminCount()
if result, err := ss.User().AnalyticsGetSystemAdminCount(); err != nil { require.Nil(t, err)
t.Fatal(err)
} else {
countBefore = result
}
u1 := model.User{} u1 := model.User{}
u1.Email = MakeEmail() u1.Email = MakeEmail()
@@ -3554,24 +3489,19 @@ func testUserStoreAnalyticsGetSystemAdminCount(t *testing.T, ss store.Store) {
u2.Email = MakeEmail() u2.Email = MakeEmail()
u2.Username = model.NewId() u2.Username = model.NewId()
if _, err := ss.User().Save(&u1); err != nil { _, err = ss.User().Save(&u1)
t.Fatal("couldn't save user", err) require.Nil(t, err, "couldn't save user")
}
defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }()
if _, err := ss.User().Save(&u2); err != nil { _, err = ss.User().Save(&u2)
t.Fatal("couldn't save user", err) require.Nil(t, err, "couldn't save user")
}
defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }() defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }()
if result, err := ss.User().AnalyticsGetSystemAdminCount(); err != nil { result, err := ss.User().AnalyticsGetSystemAdminCount()
t.Fatal(err) require.Nil(t, err)
} else { require.Equal(t, countBefore+1, result, "Did not get the expected number of system admins.")
// We expect to find 1 more system admin than there was at the start of this test function.
if count := result; count != countBefore+1 {
t.Fatal("Did not get the expected number of system admins. Expected, got: ", countBefore+1, count)
}
}
} }
func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) { func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
@@ -4048,9 +3978,8 @@ func testUserStoreGetTeamGroupUsers(t *testing.T, ss store.Store) {
require.NotNil(t, user) require.NotNil(t, user)
testUsers = append(testUsers, user) testUsers = append(testUsers, user)
} }
userGroupA := testUsers[0] require.Len(t, testUsers, 3, "testUsers length doesn't meet required length")
userGroupB := testUsers[1] userGroupA, userGroupB, userNoGroup := testUsers[0], testUsers[1], testUsers[2]
userNoGroup := testUsers[2]
// add non-group-member to the team (to prove that the query isn't just returning all members) // add non-group-member to the team (to prove that the query isn't just returning all members)
_, err = ss.Team().SaveMember(&model.TeamMember{ _, err = ss.Team().SaveMember(&model.TeamMember{
@@ -4075,8 +4004,8 @@ func testUserStoreGetTeamGroupUsers(t *testing.T, ss store.Store) {
require.NotNil(t, group) require.NotNil(t, group)
testGroups = append(testGroups, group) testGroups = append(testGroups, group)
} }
groupA := testGroups[0] require.Len(t, testGroups, 2, "testGroups length doesn't meet required length")
groupB := testGroups[1] groupA, groupB := testGroups[0], testGroups[1]
// add members to groups // add members to groups
_, err = ss.Group().UpsertMember(groupA.Id, userGroupA.Id) _, err = ss.Group().UpsertMember(groupA.Id, userGroupA.Id)
@@ -4169,9 +4098,8 @@ func testUserStoreGetChannelGroupUsers(t *testing.T, ss store.Store) {
require.NotNil(t, user) require.NotNil(t, user)
testUsers = append(testUsers, user) testUsers = append(testUsers, user)
} }
userGroupA := testUsers[0] require.Len(t, testUsers, 3, "testUsers length doesn't meet required length")
userGroupB := testUsers[1] userGroupA, userGroupB, userNoGroup := testUsers[0], testUsers[1], testUsers[2]
userNoGroup := testUsers[2]
// add non-group-member to the channel (to prove that the query isn't just returning all members) // add non-group-member to the channel (to prove that the query isn't just returning all members)
_, err = ss.Channel().SaveMember(&model.ChannelMember{ _, err = ss.Channel().SaveMember(&model.ChannelMember{
@@ -4196,8 +4124,8 @@ func testUserStoreGetChannelGroupUsers(t *testing.T, ss store.Store) {
require.NotNil(t, group) require.NotNil(t, group)
testGroups = append(testGroups, group) testGroups = append(testGroups, group)
} }
groupA := testGroups[0] require.Len(t, testGroups, 2, "testGroups length doesn't meet required length")
groupB := testGroups[1] groupA, groupB := testGroups[0], testGroups[1]
// add members to groups // add members to groups
_, err = ss.Group().UpsertMember(groupA.Id, userGroupA.Id) _, err = ss.Group().UpsertMember(groupA.Id, userGroupA.Id)

Просмотреть файл

@@ -315,22 +315,3 @@ func (w *Web) ApiSessionRequired(h func(*Context, http.ResponseWriter, *http.Req
} }
return handler return handler
} }
// apiHandlerTrustRequester provides a handler for API endpoints which do not require the user to be logged in and are
// allowed to be requested directly rather than via javascript/XMLHttpRequest, such as site branding images or the
// websocket.
func (w *Web) apiHandlerTrustRequester(h func(*Context, http.ResponseWriter, *http.Request)) http.Handler {
handler := &Handler{
GetGlobalAppOptions: w.GetGlobalAppOptions,
HandleFunc: h,
HandlerName: GetHandlerName(h),
RequireSession: false,
TrustRequester: true,
RequireMfa: false,
IsStatic: false,
}
if *w.ConfigService.Config().ServiceSettings.WebserverMode == "gzip" {
return gziphandler.GzipHandler(handler)
}
return handler
}

Просмотреть файл

@@ -47,7 +47,7 @@ func New(config configservice.ConfigService, globalOptions app.AppOptionCreator,
// -1 means that the browser is not supported in any version. // -1 means that the browser is not supported in any version.
var browserMinimumSupported = map[string]int{ var browserMinimumSupported = map[string]int{
"BrowserIE": 12, "BrowserIE": 12,
"BrowserSafari": 9, "BrowserSafari": 12,
} }
func CheckClientCompatability(agentString string) bool { func CheckClientCompatability(agentString string) bool {

Просмотреть файл

@@ -245,9 +245,13 @@ func TestCheckClientCompatability(t *testing.T) {
{"Internet Explorer 11 2", "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Zoom 3.6.0; rv:11.0) like Gecko", false}, {"Internet Explorer 11 2", "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Zoom 3.6.0; rv:11.0) like Gecko", false},
{"Internet Explorer 11 (Compatibility Mode) 1", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET CLR 1.1.4322; InfoPath.3; Zoom 3.6.0)", false}, {"Internet Explorer 11 (Compatibility Mode) 1", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; .NET CLR 1.1.4322; InfoPath.3; Zoom 3.6.0)", false},
{"Internet Explorer 11 (Compatibility Mode) 2", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Zoom 3.6.0)", false}, {"Internet Explorer 11 (Compatibility Mode) 2", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; Zoom 3.6.0)", false},
{"Safari 9", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38", true}, {"Safari 12", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15", true},
{"Safari 11", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38", false},
{"Safari 10", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8", false},
{"Safari 9", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4", false},
{"Safari 8", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12", false}, {"Safari 8", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/600.7.12 (KHTML, like Gecko) Version/8.0.7 Safari/600.7.12", false},
{"Safari Mobile", "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B137 Safari/601.1", true}, {"Safari Mobile 12", "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like macOS) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/12.0 Mobile/14A5335d Safari/602.1.50", true},
{"Safari Mobile 9", "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B137 Safari/601.1", false},
} }
for _, browser := range uaTestParameters { for _, browser := range uaTestParameters {
t.Run(browser.Name, func(t *testing.T) { t.Run(browser.Name, func(t *testing.T) {