Merge remote-tracking branch 'upstream/release-5.2' into release-5.2-daily-merge-20180808
Этот коммит содержится в:
@@ -285,6 +285,7 @@ func getFilePreview(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
c.Err.StatusCode = http.StatusNotFound
|
c.Err.StatusCode = http.StatusNotFound
|
||||||
|
return
|
||||||
}
|
}
|
||||||
defer fileReader.Close()
|
defer fileReader.Close()
|
||||||
|
|
||||||
|
|||||||
18
app/app.go
18
app/app.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -212,6 +213,10 @@ func New(options ...Option) (outApp *App, outErr error) {
|
|||||||
return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
|
return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := app.ensureInstallationDate(); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "unable to ensure installation date")
|
||||||
|
}
|
||||||
|
|
||||||
app.EnsureDiagnosticId()
|
app.EnsureDiagnosticId()
|
||||||
app.regenerateClientConfig()
|
app.regenerateClientConfig()
|
||||||
|
|
||||||
@@ -740,3 +745,16 @@ func (a *App) StartElasticsearch() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) getSystemInstallDate() (int64, *model.AppError) {
|
||||||
|
result := <-a.Srv.Store.System().GetByName(model.SYSTEM_INSTALLATION_DATE_KEY)
|
||||||
|
if result.Err != nil {
|
||||||
|
return 0, result.Err
|
||||||
|
}
|
||||||
|
systemData := result.Data.(*model.System)
|
||||||
|
value, err := strconv.ParseInt(systemData.Value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, model.NewAppError("getSystemInstallDate", "app.system_install_date.parse_int.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/mlog"
|
"github.com/mattermost/mattermost-server/mlog"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
@@ -208,6 +209,30 @@ func (a *App) ensureAsymmetricSigningKey() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) ensureInstallationDate() error {
|
||||||
|
_, err := a.getSystemInstallDate()
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := <-a.Srv.Store.User().InferSystemInstallDate()
|
||||||
|
var installationDate int64
|
||||||
|
if result.Err == nil && result.Data.(int64) > 0 {
|
||||||
|
installationDate = result.Data.(int64)
|
||||||
|
} else {
|
||||||
|
installationDate = utils.MillisFromTime(time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
result = <-a.Srv.Store.System().SaveOrUpdate(&model.System{
|
||||||
|
Name: model.SYSTEM_INSTALLATION_DATE_KEY,
|
||||||
|
Value: strconv.FormatInt(installationDate, 10),
|
||||||
|
})
|
||||||
|
if result.Err != nil {
|
||||||
|
return result.Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
|
// AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
|
||||||
func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
|
func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
|
||||||
return a.asymmetricSigningKey
|
return a.asymmetricSigningKey
|
||||||
@@ -296,6 +321,10 @@ func (a *App) ClientConfigWithComputed() map[string]string {
|
|||||||
// by the client.
|
// by the client.
|
||||||
respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
|
respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
|
||||||
respCfg["MaxPostSize"] = strconv.Itoa(a.MaxPostSize())
|
respCfg["MaxPostSize"] = strconv.Itoa(a.MaxPostSize())
|
||||||
|
respCfg["InstallationDate"] = ""
|
||||||
|
if installationDate, err := a.getSystemInstallDate(); err == nil {
|
||||||
|
respCfg["InstallationDate"] = strconv.FormatInt(installationDate, 10)
|
||||||
|
}
|
||||||
|
|
||||||
return respCfg
|
return respCfg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,15 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||||
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigListener(t *testing.T) {
|
func TestConfigListener(t *testing.T) {
|
||||||
@@ -76,3 +80,80 @@ func TestClientConfigWithComputed(t *testing.T) {
|
|||||||
t.Fatal("expected MaxPostSize in returned config")
|
t.Fatal("expected MaxPostSize in returned config")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEnsureInstallationDate(t *testing.T) {
|
||||||
|
th := Setup()
|
||||||
|
defer th.TearDown()
|
||||||
|
|
||||||
|
tt := []struct {
|
||||||
|
Name string
|
||||||
|
PrevInstallationDate *int64
|
||||||
|
UsersCreationDates []int64
|
||||||
|
ExpectedInstallationDate *int64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
Name: "New installation: no users, no installation date",
|
||||||
|
PrevInstallationDate: nil,
|
||||||
|
UsersCreationDates: nil,
|
||||||
|
ExpectedInstallationDate: model.NewInt64(utils.MillisFromTime(time.Now())),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Old installation: users, no installation date",
|
||||||
|
PrevInstallationDate: nil,
|
||||||
|
UsersCreationDates: []int64{10000000000, 30000000000, 20000000000},
|
||||||
|
ExpectedInstallationDate: model.NewInt64(10000000000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "New installation, second run: no users, installation date",
|
||||||
|
PrevInstallationDate: model.NewInt64(80000000000),
|
||||||
|
UsersCreationDates: []int64{10000000000, 30000000000, 20000000000},
|
||||||
|
ExpectedInstallationDate: model.NewInt64(80000000000),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "Old installation already updated: users, installation date",
|
||||||
|
PrevInstallationDate: model.NewInt64(90000000000),
|
||||||
|
UsersCreationDates: []int64{10000000000, 30000000000, 20000000000},
|
||||||
|
ExpectedInstallationDate: model.NewInt64(90000000000),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tt {
|
||||||
|
t.Run(tc.Name, func(t *testing.T) {
|
||||||
|
sqlStore := th.App.Srv.Store.User().(*sqlstore.SqlUserStore)
|
||||||
|
sqlStore.GetMaster().Exec("DELETE FROM Users")
|
||||||
|
|
||||||
|
var users []*model.User
|
||||||
|
for _, createAt := range tc.UsersCreationDates {
|
||||||
|
user := th.CreateUser()
|
||||||
|
user.CreateAt = createAt
|
||||||
|
sqlStore.GetMaster().Exec("UPDATE Users SET CreateAt = :CreateAt WHERE Id = :UserId", map[string]interface{}{"CreateAt": createAt, "UserId": user.Id})
|
||||||
|
users = append(users, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.PrevInstallationDate == nil {
|
||||||
|
<-th.App.Srv.Store.System().PermanentDeleteByName(model.SYSTEM_INSTALLATION_DATE_KEY)
|
||||||
|
} else {
|
||||||
|
<-th.App.Srv.Store.System().SaveOrUpdate(&model.System{
|
||||||
|
Name: model.SYSTEM_INSTALLATION_DATE_KEY,
|
||||||
|
Value: strconv.FormatInt(*tc.PrevInstallationDate, 10),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
err := th.App.ensureInstallationDate()
|
||||||
|
|
||||||
|
if tc.ExpectedInstallationDate == nil {
|
||||||
|
assert.Error(t, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
result := <-th.App.Srv.Store.System().GetByName(model.SYSTEM_INSTALLATION_DATE_KEY)
|
||||||
|
assert.Nil(t, result.Err)
|
||||||
|
data, _ := result.Data.(*model.System)
|
||||||
|
value, _ := strconv.ParseInt(data.Value, 10, 64)
|
||||||
|
assert.True(t, *tc.ExpectedInstallationDate <= value && *tc.ExpectedInstallationDate+1000 >= value)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlStore.GetMaster().Exec("DELETE FROM Users")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -763,7 +763,7 @@ func (a *App) ImportReply(data *ReplyImportData, post *model.Post, teamId string
|
|||||||
|
|
||||||
var reply *model.Post
|
var reply *model.Post
|
||||||
for _, r := range replies {
|
for _, r := range replies {
|
||||||
if r.Message == *data.Message {
|
if r.Message == *data.Message && r.RootId == post.Id {
|
||||||
reply = r
|
reply = r
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -784,7 +784,7 @@ func (a *App) ImportReply(data *ReplyImportData, post *model.Post, teamId string
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
reply.FileIds = fileIds
|
reply.FileIds = append(reply.FileIds, fileIds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if reply.Id == "" {
|
if reply.Id == "" {
|
||||||
@@ -820,6 +820,8 @@ func (a *App) ImportAttachment(data *AttachmentImportData, post *model.Post, tea
|
|||||||
return nil, fileUploadError
|
return nil, fileUploadError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.HandleImages([]string{fileInfo.PreviewPath}, []string{fileInfo.ThumbnailPath}, [][]byte{buf.Bytes()})
|
||||||
|
|
||||||
mlog.Info(fmt.Sprintf("uploading file with name %s", file.Name()))
|
mlog.Info(fmt.Sprintf("uploading file with name %s", file.Name()))
|
||||||
return fileInfo, nil
|
return fileInfo, nil
|
||||||
}
|
}
|
||||||
@@ -889,7 +891,7 @@ func (a *App) ImportPost(data *PostImportData, dryRun bool) *model.AppError {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
post.FileIds = fileIds
|
post.FileIds = append(post.FileIds, fileIds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if post.Id == "" {
|
if post.Id == "" {
|
||||||
|
|||||||
@@ -1700,6 +1700,60 @@ func TestImportImportPost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update post with replies.
|
||||||
|
data = &PostImportData{
|
||||||
|
Team: &teamName,
|
||||||
|
Channel: &channelName,
|
||||||
|
User: &user2.Username,
|
||||||
|
Message: ptrStr("Message with reply"),
|
||||||
|
CreateAt: &replyPostTime,
|
||||||
|
Replies: &[]ReplyImportData{{
|
||||||
|
User: &username,
|
||||||
|
Message: ptrStr("Message reply"),
|
||||||
|
CreateAt: &replyTime,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
if err := th.App.ImportPost(data, false); err != nil {
|
||||||
|
t.Fatalf("Expected success.")
|
||||||
|
}
|
||||||
|
AssertAllPostsCount(t, th.App, initialPostCount, 8, team.Id)
|
||||||
|
|
||||||
|
// Create new post with replies based on the previous one.
|
||||||
|
data = &PostImportData{
|
||||||
|
Team: &teamName,
|
||||||
|
Channel: &channelName,
|
||||||
|
User: &user2.Username,
|
||||||
|
Message: ptrStr("Message with reply 2"),
|
||||||
|
CreateAt: &replyPostTime,
|
||||||
|
Replies: &[]ReplyImportData{{
|
||||||
|
User: &username,
|
||||||
|
Message: ptrStr("Message reply"),
|
||||||
|
CreateAt: &replyTime,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
if err := th.App.ImportPost(data, false); err != nil {
|
||||||
|
t.Fatalf("Expected success.")
|
||||||
|
}
|
||||||
|
AssertAllPostsCount(t, th.App, initialPostCount, 10, team.Id)
|
||||||
|
|
||||||
|
// Create new reply for existing post with replies.
|
||||||
|
data = &PostImportData{
|
||||||
|
Team: &teamName,
|
||||||
|
Channel: &channelName,
|
||||||
|
User: &user2.Username,
|
||||||
|
Message: ptrStr("Message with reply"),
|
||||||
|
CreateAt: &replyPostTime,
|
||||||
|
Replies: &[]ReplyImportData{{
|
||||||
|
User: &username,
|
||||||
|
Message: ptrStr("Message reply 2"),
|
||||||
|
CreateAt: &replyTime,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
if err := th.App.ImportPost(data, false); err != nil {
|
||||||
|
t.Fatalf("Expected success.")
|
||||||
|
}
|
||||||
|
AssertAllPostsCount(t, th.App, initialPostCount, 11, team.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestImportImportDirectChannel(t *testing.T) {
|
func TestImportImportDirectChannel(t *testing.T) {
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ func TestHubStopWithMultipleConnections(t *testing.T) {
|
|||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
th.App.HubStart()
|
th.App.HubStart()
|
||||||
registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
wc1 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
||||||
registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
wc2 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
||||||
registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
wc3 := registerDummyWebConn(t, th.App, s.Listener.Addr(), th.BasicUser.Id)
|
||||||
th.App.HubStop()
|
defer wc1.Close()
|
||||||
|
defer wc2.Close()
|
||||||
|
defer wc3.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,25 +44,15 @@ var ActianceExportCmd = &cobra.Command{
|
|||||||
RunE: buildExportCmdF("actiance"),
|
RunE: buildExportCmdF("actiance"),
|
||||||
}
|
}
|
||||||
|
|
||||||
var GlobalRelayExportCmd = &cobra.Command{
|
|
||||||
Use: "global-relay",
|
|
||||||
Short: "Export data from Mattermost in Global Relay format",
|
|
||||||
Long: "Export data from Mattermost in Global Relay format",
|
|
||||||
Example: "export global-relay --exportFrom=12345",
|
|
||||||
RunE: buildExportCmdF("globalrelay"),
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ScheduleExportCmd.Flags().String("format", "actiance", "The format to export data")
|
ScheduleExportCmd.Flags().String("format", "actiance", "The format to export data")
|
||||||
ScheduleExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
ScheduleExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
||||||
ScheduleExportCmd.Flags().Int("timeoutSeconds", -1, "The maximum number of seconds to wait for the job to complete before timing out.")
|
ScheduleExportCmd.Flags().Int("timeoutSeconds", -1, "The maximum number of seconds to wait for the job to complete before timing out.")
|
||||||
CsvExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
CsvExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
||||||
ActianceExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
ActianceExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
||||||
GlobalRelayExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
|
||||||
MessageExportCmd.AddCommand(ScheduleExportCmd)
|
MessageExportCmd.AddCommand(ScheduleExportCmd)
|
||||||
MessageExportCmd.AddCommand(CsvExportCmd)
|
MessageExportCmd.AddCommand(CsvExportCmd)
|
||||||
MessageExportCmd.AddCommand(ActianceExportCmd)
|
MessageExportCmd.AddCommand(ActianceExportCmd)
|
||||||
MessageExportCmd.AddCommand(GlobalRelayExportCmd)
|
|
||||||
RootCmd.AddCommand(MessageExportCmd)
|
RootCmd.AddCommand(MessageExportCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3110,6 +3110,10 @@
|
|||||||
"id": "app.schemes.is_phase_2_migration_completed.not_completed.app_error",
|
"id": "app.schemes.is_phase_2_migration_completed.not_completed.app_error",
|
||||||
"translation": "This API endpoint is not accessible as required migrations have not yet completed."
|
"translation": "This API endpoint is not accessible as required migrations have not yet completed."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "app.system_install_date.parse_int.app_error",
|
||||||
|
"translation": "Failed to parse installation date"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "app.team.join_user_to_team.max_accounts.app_error",
|
"id": "app.team.join_user_to_team.max_accounts.app_error",
|
||||||
"translation": "This team has reached the maximum number of allowed accounts. Contact your systems administrator to set a higher limit."
|
"translation": "This team has reached the maximum number of allowed accounts. Contact your systems administrator to set a higher limit."
|
||||||
@@ -5938,6 +5942,10 @@
|
|||||||
"id": "store.sql_user.get_for_login.multiple_users",
|
"id": "store.sql_user.get_for_login.multiple_users",
|
||||||
"translation": "We found multiple users matching your credentials and were unable to log you in. Please contact an administrator."
|
"translation": "We found multiple users matching your credentials and were unable to log you in. Please contact an administrator."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "store.sql_user.get_system_install_date.app_error",
|
||||||
|
"translation": "Unable to infer the system date based on the first user creation date."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "store.sql_user.get_new_users.app_error",
|
"id": "store.sql_user.get_new_users.app_error",
|
||||||
"translation": "We encountered an error while finding the new users"
|
"translation": "We encountered an error while finding the new users"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const (
|
|||||||
SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
|
SYSTEM_ACTIVE_LICENSE_ID = "ActiveLicenseId"
|
||||||
SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
|
SYSTEM_LAST_COMPLIANCE_TIME = "LastComplianceTime"
|
||||||
SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey"
|
SYSTEM_ASYMMETRIC_SIGNING_KEY = "AsymmetricSigningKey"
|
||||||
|
SYSTEM_INSTALLATION_DATE_KEY = "InstallationDate"
|
||||||
)
|
)
|
||||||
|
|
||||||
type System struct {
|
type System struct {
|
||||||
|
|||||||
11
plugin/plugintest/doc.go
Обычный файл
11
plugin/plugintest/doc.go
Обычный файл
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See License.txt for license information.
|
||||||
|
|
||||||
|
// The plugintest package provides mocks that can be used to test plugins.
|
||||||
|
//
|
||||||
|
// The mocks are created using testify's mock package:
|
||||||
|
// https://godoc.org/github.com/stretchr/testify/mock
|
||||||
|
//
|
||||||
|
// If you need to import the mock package, you can import it with
|
||||||
|
// "github.com/mattermost/mattermost-server/plugin/plugintest/mock".
|
||||||
|
package plugintest
|
||||||
55
plugin/plugintest/example_hello_user_test.go
Обычный файл
55
plugin/plugintest/example_hello_user_test.go
Обычный файл
@@ -0,0 +1,55 @@
|
|||||||
|
package plugintest_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
|
"github.com/mattermost/mattermost-server/plugin"
|
||||||
|
"github.com/mattermost/mattermost-server/plugin/plugintest"
|
||||||
|
)
|
||||||
|
|
||||||
|
type HelloUserPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *HelloUserPlugin) ServeHTTP(context *plugin.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
userId := r.Header.Get("Mattermost-User-Id")
|
||||||
|
user, err := p.API.GetUser(userId)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
p.API.LogError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, "Welcome back, %s!", user.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example() {
|
||||||
|
t := &testing.T{}
|
||||||
|
user := &model.User{
|
||||||
|
Id: model.NewId(),
|
||||||
|
Username: "billybob",
|
||||||
|
}
|
||||||
|
|
||||||
|
api := &plugintest.API{}
|
||||||
|
api.On("GetUser", user.Id).Return(user, nil)
|
||||||
|
defer api.AssertExpectations(t)
|
||||||
|
|
||||||
|
p := &HelloUserPlugin{}
|
||||||
|
p.SetAPI(api)
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
r := httptest.NewRequest("GET", "/", nil)
|
||||||
|
r.Header.Add("Mattermost-User-Id", user.Id)
|
||||||
|
p.ServeHTTP(&plugin.Context{}, w, r)
|
||||||
|
body, err := ioutil.ReadAll(w.Result().Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, "Welcome back, billybob!", string(body))
|
||||||
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
|
||||||
// See License.txt for license information.
|
|
||||||
|
|
||||||
// The plugintest package provides mocks that can be used to test plugins. For example, to test the
|
|
||||||
// ServeHTTP method of the plugin package's HelloUser example:
|
|
||||||
//
|
|
||||||
// package plugin_test
|
|
||||||
//
|
|
||||||
// import (
|
|
||||||
// "io/ioutil"
|
|
||||||
// "net/http/httptest"
|
|
||||||
// "testing"
|
|
||||||
//
|
|
||||||
// "github.com/stretchr/testify/assert"
|
|
||||||
// "github.com/stretchr/testify/require"
|
|
||||||
//
|
|
||||||
// "github.com/mattermost/mattermost-server/model"
|
|
||||||
// "github.com/mattermost/mattermost-server/plugin/plugintest"
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func TestHelloUserPlugin(t *testing.T) {
|
|
||||||
// user := &model.User{
|
|
||||||
// Id: model.NewId(),
|
|
||||||
// Username: "billybob",
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// api := &plugintest.API{}
|
|
||||||
// api.On("GetUser", user.Id).Return(user, nil)
|
|
||||||
// defer api.AssertExpectations(t)
|
|
||||||
//
|
|
||||||
// p := &HelloUserPlugin{}
|
|
||||||
// p.OnActivate(api)
|
|
||||||
//
|
|
||||||
// w := httptest.NewRecorder()
|
|
||||||
// r := httptest.NewRequest("GET", "/", nil)
|
|
||||||
// r.Header.Add("Mattermost-User-Id", user.Id)
|
|
||||||
// p.ServeHTTP(w, r)
|
|
||||||
// body, err := ioutil.ReadAll(w.Result().Body)
|
|
||||||
// require.NoError(t, err)
|
|
||||||
// assert.Equal(t, "Welcome back, billybob!", string(body))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// The mocks are created using testify's mock package:
|
|
||||||
// https://godoc.org/github.com/stretchr/testify/mock
|
|
||||||
//
|
|
||||||
// If you need to import the mock package, you can import it with
|
|
||||||
// "github.com/mattermost/mattermost-server/plugin/plugintest/mock".
|
|
||||||
package plugintest
|
|
||||||
@@ -1316,3 +1316,14 @@ func (us SqlUserStore) ClearAllCustomRoleAssignments() store.StoreChannel {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (us SqlUserStore) InferSystemInstallDate() store.StoreChannel {
|
||||||
|
return store.Do(func(result *store.StoreResult) {
|
||||||
|
createAt, err := us.GetReplica().SelectInt("SELECT CreateAt FROM Users WHERE CreateAt IS NOT NULL ORDER BY CreateAt ASC LIMIT 1")
|
||||||
|
if err != nil {
|
||||||
|
result.Err = model.NewAppError("SqlUserStore.GetSystemInstallDate", "store.sql_user.get_system_install_date.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result.Data = createAt
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -263,6 +263,7 @@ type UserStore interface {
|
|||||||
GetProfilesNotInTeam(teamId string, offset int, limit int) StoreChannel
|
GetProfilesNotInTeam(teamId string, offset int, limit int) StoreChannel
|
||||||
GetEtagForProfilesNotInTeam(teamId string) StoreChannel
|
GetEtagForProfilesNotInTeam(teamId string) StoreChannel
|
||||||
ClearAllCustomRoleAssignments() StoreChannel
|
ClearAllCustomRoleAssignments() StoreChannel
|
||||||
|
InferSystemInstallDate() StoreChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionStore interface {
|
type SessionStore interface {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
@@ -45,9 +45,9 @@ func (_m *ChannelStore) AnalyticsTypeCount(teamId string, channelType string) st
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// AutocompleteInTeam provides a mock function with given fields: teamId, term
|
// AutocompleteInTeam provides a mock function with given fields: teamId, term, includeDeleted
|
||||||
func (_m *ChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
func (_m *ChannelStore) AutocompleteInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
||||||
ret := _m.Called(teamId, term)
|
ret := _m.Called(teamId, term, includeDeleted)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
|
||||||
@@ -146,9 +146,9 @@ func (_m *ChannelStore) GetAll(teamId string) store.StoreChannel {
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAllChannelMembersForUser provides a mock function with given fields: userId, allowFromCache
|
// GetAllChannelMembersForUser provides a mock function with given fields: userId, allowFromCache, includeDeleted
|
||||||
func (_m *ChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) store.StoreChannel {
|
func (_m *ChannelStore) GetAllChannelMembersForUser(userId string, allowFromCache bool, includeDeleted bool) store.StoreChannel {
|
||||||
ret := _m.Called(userId, allowFromCache)
|
ret := _m.Called(userId, allowFromCache, includeDeleted)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, bool, bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, bool, bool) store.StoreChannel); ok {
|
||||||
@@ -258,9 +258,9 @@ func (_m *ChannelStore) GetChannelUnread(channelId string, userId string) store.
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetChannels provides a mock function with given fields: teamId, userId
|
// GetChannels provides a mock function with given fields: teamId, userId, includeDeleted
|
||||||
func (_m *ChannelStore) GetChannels(teamId string, userId string, includeDeleted bool) store.StoreChannel {
|
func (_m *ChannelStore) GetChannels(teamId string, userId string, includeDeleted bool) store.StoreChannel {
|
||||||
ret := _m.Called(teamId, userId)
|
ret := _m.Called(teamId, userId, includeDeleted)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
|
||||||
@@ -775,9 +775,9 @@ func (_m *ChannelStore) SaveMember(member *model.ChannelMember) store.StoreChann
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchInTeam provides a mock function with given fields: teamId, term
|
// SearchInTeam provides a mock function with given fields: teamId, term, includeDeleted
|
||||||
func (_m *ChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
func (_m *ChannelStore) SearchInTeam(teamId string, term string, includeDeleted bool) store.StoreChannel {
|
||||||
ret := _m.Called(teamId, term)
|
ret := _m.Called(teamId, term, includeDeleted)
|
||||||
|
|
||||||
var r0 store.StoreChannel
|
var r0 store.StoreChannel
|
||||||
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
|
if rf, ok := ret.Get(0).(func(string, string, bool) store.StoreChannel); ok {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
@@ -514,6 +514,22 @@ func (_m *UserStore) GetUnreadCountForChannel(userId string, channelId string) s
|
|||||||
return r0
|
return r0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InferSystemInstallDate provides a mock function with given fields:
|
||||||
|
func (_m *UserStore) InferSystemInstallDate() store.StoreChannel {
|
||||||
|
ret := _m.Called()
|
||||||
|
|
||||||
|
var r0 store.StoreChannel
|
||||||
|
if rf, ok := ret.Get(0).(func() store.StoreChannel); ok {
|
||||||
|
r0 = rf()
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(store.StoreChannel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// InvalidatProfileCacheForUser provides a mock function with given fields: userId
|
// InvalidatProfileCacheForUser provides a mock function with given fields: userId
|
||||||
func (_m *UserStore) InvalidatProfileCacheForUser(userId string) {
|
func (_m *UserStore) InvalidatProfileCacheForUser(userId string) {
|
||||||
_m.Called(userId)
|
_m.Called(userId)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Code generated by mockery v1.0.0
|
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||||
|
|
||||||
// Regenerate this file using `make store-mocks`.
|
// Regenerate this file using `make store-mocks`.
|
||||||
|
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user