Этот коммит содержится в:
Eli Yukelzon
2020-10-02 11:14:57 +03:00
коммит произвёл GitHub
родитель 2f47cf5994
Коммит b8ceb610e6
12 изменённых файлов: 256 добавлений и 72 удалений

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

@@ -215,51 +215,57 @@ func TestUploadFiles(t *testing.T) {
expectedImageThumbnailNames []string expectedImageThumbnailNames []string
expectedImagePreviewNames []string expectedImagePreviewNames []string
expectedImageHasPreview []bool expectedImageHasPreview []bool
expectedImageMiniPreview []bool
setupConfig func(a *app.App) func(a *app.App) setupConfig func(a *app.App) func(a *app.App)
checkResponse func(t *testing.T, resp *model.Response) checkResponse func(t *testing.T, resp *model.Response)
}{ }{
// Upload a bunch of files, mixed images and non-images // Upload a bunch of files, mixed images and non-images
{ {
title: "Happy", title: "Happy",
names: []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"}, names: []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
expectedImageMiniPreview: []bool{true, true, false, false, true},
}, },
// Upload a bunch of files, with clientIds // Upload a bunch of files, with clientIds
{ {
title: "Happy client_ids", title: "Happy client_ids",
names: []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"}, names: []string{"test.png", "testgif.gif", "testplugin.tar.gz", "test-search.md", "test.tiff"},
clientIds: []string{"1", "2", "3", "4", "5"}, clientIds: []string{"1", "2", "3", "4", "5"},
expectedCreatorId: th.BasicUser.Id, expectedImageMiniPreview: []bool{true, true, false, false, true},
expectedCreatorId: th.BasicUser.Id,
}, },
// Upload a bunch of images. testgif.gif is an animated GIF, // Upload a bunch of images. testgif.gif is an animated GIF,
// so it does not have HasPreviewImage set. // so it does not have HasPreviewImage set.
{ {
title: "Happy images", title: "Happy images",
names: []string{"test.png", "testgif.gif"}, names: []string{"test.png", "testgif.gif"},
expectImage: true, expectImage: true,
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
expectedImageWidths: []int{408, 118}, expectedImageWidths: []int{408, 118},
expectedImageHeights: []int{336, 118}, expectedImageHeights: []int{336, 118},
expectedImageHasPreview: []bool{true, false}, expectedImageHasPreview: []bool{true, false},
expectedImageMiniPreview: []bool{true, true},
}, },
{ {
title: "Happy invalid image", title: "Happy invalid image",
names: []string{"testgif.gif"}, names: []string{"testgif.gif"},
blobs: [][]byte{fileBytes(t, "test-search.md")}, blobs: [][]byte{fileBytes(t, "test-search.md")},
skipPayloadValidation: true, skipPayloadValidation: true,
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
expectedImageMiniPreview: []bool{false},
}, },
// Simple POST, chunked encoding // Simple POST, chunked encoding
{ {
title: "Happy image chunked post", title: "Happy image chunked post",
skipMultipart: true, skipMultipart: true,
useChunkedInSimplePost: true, useChunkedInSimplePost: true,
names: []string{"test.png"}, names: []string{"test.png"},
expectImage: true, expectImage: true,
expectedImageWidths: []int{408}, expectedImageWidths: []int{408},
expectedImageHeights: []int{336}, expectedImageHeights: []int{336},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
expectedImageMiniPreview: []bool{true},
}, },
// Image thumbnail and preview: size and orientation. Note that // Image thumbnail and preview: size and orientation. Note that
// the expected image dimensions remain the same regardless of the // the expected image dimensions remain the same regardless of the
@@ -275,6 +281,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
expectedImageMiniPreview: []bool{true},
}, },
{ {
title: "Happy image thumbnail/preview 2", title: "Happy image thumbnail/preview 2",
@@ -285,6 +292,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{2860}, expectedImageWidths: []int{2860},
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
{ {
@@ -296,6 +304,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{2860}, expectedImageWidths: []int{2860},
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
{ {
@@ -307,6 +316,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{2860}, expectedImageWidths: []int{2860},
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
{ {
@@ -318,6 +328,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{2860}, expectedImageWidths: []int{2860},
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
{ {
@@ -329,6 +340,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{2860}, expectedImageWidths: []int{2860},
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
{ {
@@ -340,6 +352,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{2860}, expectedImageWidths: []int{2860},
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
{ {
@@ -351,6 +364,7 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{2860}, expectedImageWidths: []int{2860},
expectedImageHeights: []int{1578}, expectedImageHeights: []int{1578},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
// TIFF preview test // TIFF preview test
@@ -363,13 +377,15 @@ func TestUploadFiles(t *testing.T) {
expectedImageWidths: []int{701}, expectedImageWidths: []int{701},
expectedImageHeights: []int{701}, expectedImageHeights: []int{701},
expectedImageHasPreview: []bool{true}, expectedImageHasPreview: []bool{true},
expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.BasicUser.Id, expectedCreatorId: th.BasicUser.Id,
}, },
{ {
title: "Happy admin", title: "Happy admin",
client: th.SystemAdminClient, client: th.SystemAdminClient,
names: []string{"test.png"}, names: []string{"test.png"},
expectedCreatorId: th.SystemAdminUser.Id, expectedImageMiniPreview: []bool{true},
expectedCreatorId: th.SystemAdminUser.Id,
}, },
{ {
title: "Happy stream", title: "Happy stream",
@@ -384,7 +400,8 @@ func TestUploadFiles(t *testing.T) {
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize }) a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = maxFileSize })
} }
}, },
expectedCreatorId: th.BasicUser.Id, expectedImageMiniPreview: []bool{false},
expectedCreatorId: th.BasicUser.Id,
}, },
// Error cases // Error cases
{ {
@@ -397,19 +414,21 @@ func TestUploadFiles(t *testing.T) {
{ {
// on simple post this uploads the last file // on simple post this uploads the last file
// successfully, without a ClientId // successfully, without a ClientId
title: "Error too few client_ids", title: "Error too few client_ids",
skipSimplePost: true, skipSimplePost: true,
names: []string{"test.png", "testplugin.tar.gz", "test-search.md"}, names: []string{"test.png", "testplugin.tar.gz", "test-search.md"},
clientIds: []string{"1", "4"}, clientIds: []string{"1", "4"},
skipSuccessValidation: true, expectedImageMiniPreview: []bool{true, false, false},
checkResponse: CheckBadRequestStatus, skipSuccessValidation: true,
checkResponse: CheckBadRequestStatus,
}, },
{ {
title: "Error invalid channel_id", title: "Error invalid channel_id",
channelId: "../../junk", channelId: "../../junk",
names: []string{"test.png"}, names: []string{"test.png"},
skipSuccessValidation: true, expectedImageMiniPreview: []bool{true},
checkResponse: CheckBadRequestStatus, skipSuccessValidation: true,
checkResponse: CheckBadRequestStatus,
}, },
{ {
title: "Error admin channel_id does not exist", title: "Error admin channel_id does not exist",
@@ -456,12 +475,13 @@ func TestUploadFiles(t *testing.T) {
}, },
// File too large (chunked, simple POST only, multipart would've been redundant with above) // File too large (chunked, simple POST only, multipart would've been redundant with above)
{ {
title: "File too large chunked", title: "File too large chunked",
useChunkedInSimplePost: true, useChunkedInSimplePost: true,
skipMultipart: true, skipMultipart: true,
names: []string{"test.png"}, names: []string{"test.png"},
skipSuccessValidation: true, skipSuccessValidation: true,
checkResponse: CheckRequestEntityTooLargeStatus, checkResponse: CheckRequestEntityTooLargeStatus,
expectedImageMiniPreview: []bool{false},
setupConfig: func(a *app.App) func(a *app.App) { setupConfig: func(a *app.App) func(a *app.App) {
maxFileSize := *a.Config().FileSettings.MaxFileSize maxFileSize := *a.Config().FileSettings.MaxFileSize
a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 279590 }) a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxFileSize = 279590 })
@@ -560,6 +580,9 @@ func TestUploadFiles(t *testing.T) {
assert.Equal(t, ri.Path, "", "File path should not be set on returned info") assert.Equal(t, ri.Path, "", "File path should not be set on returned info")
assert.Equal(t, ri.ThumbnailPath, "", "File thumbnail path should not be set on returned info") assert.Equal(t, ri.ThumbnailPath, "", "File thumbnail path should not be set on returned info")
assert.Equal(t, ri.PreviewPath, "", "File preview path should not be set on returned info") assert.Equal(t, ri.PreviewPath, "", "File preview path should not be set on returned info")
if len(tc.expectedImageMiniPreview) == len(fileResp.FileInfos) {
assert.Equal(t, ri.MiniPreview != nil, tc.expectedImageMiniPreview[i], "File: %s mini preview state unexpected", tc.names[i])
}
if len(tc.clientIds) > i { if len(tc.clientIds) > i {
assert.True(t, len(fileResp.ClientIds) == len(tc.clientIds), assert.True(t, len(fileResp.ClientIds) == len(tc.clientIds),
fmt.Sprintf("Wrong number of clientIds returned, expected %v, got %v", len(tc.clientIds), len(fileResp.ClientIds))) fmt.Sprintf("Wrong number of clientIds returned, expected %v, got %v", len(tc.clientIds), len(fileResp.ClientIds)))

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

@@ -633,7 +633,7 @@ func (a *App) UploadFileX(channelId, name string, input io.Reader,
return t.fileinfo, aerr return t.fileinfo, aerr
} }
// Concurrently upload and update DB, and post-process the image. // Concurrently post-process the image.
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
if !t.Raw && t.fileinfo.IsImage() { if !t.Raw && t.fileinfo.IsImage() {
@@ -649,6 +649,7 @@ func (a *App) UploadFileX(channelId, name string, input io.Reader,
return nil, aerr return nil, aerr
} }
wg.Wait()
if _, err := t.saveToDatabase(t.fileinfo); err != nil { if _, err := t.saveToDatabase(t.fileinfo); err != nil {
var appErr *model.AppError var appErr *model.AppError
switch { switch {
@@ -659,8 +660,6 @@ func (a *App) UploadFileX(channelId, name string, input io.Reader,
} }
} }
wg.Wait()
return t.fileinfo, nil return t.fileinfo, nil
} }
@@ -843,7 +842,7 @@ func (t *UploadFileTask) postprocessImage() {
h := decoded.Bounds().Dy() h := decoded.Bounds().Dy()
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(2) wg.Add(3)
go func() { go func() {
defer wg.Done() defer wg.Done()
thumb := decoded thumb := decoded
@@ -865,6 +864,13 @@ func (t *UploadFileTask) postprocessImage() {
} }
writeJPEG(preview, t.fileinfo.PreviewPath) writeJPEG(preview, t.fileinfo.PreviewPath)
}() }()
go func() {
defer wg.Done()
if t.fileinfo.MiniPreview == nil {
t.fileinfo.MiniPreview = model.GenerateMiniPreviewImage(decoded)
}
}()
wg.Wait() wg.Wait()
} }
@@ -1125,6 +1131,41 @@ func (a *App) generatePreviewImage(img image.Image, previewPath string, width in
} }
} }
// generateMiniPreview updates mini preview if needed
// will save fileinfo with the preview added
func (a *App) generateMiniPreview(fi *model.FileInfo) {
if fi.IsImage() && fi.MiniPreview == nil {
data, err := a.ReadFile(fi.Path)
if err != nil {
mlog.Error("error reading image file", mlog.Err(err))
return
}
img, _, _ := prepareImage(data)
if img == nil {
return
}
fi.MiniPreview = model.GenerateMiniPreviewImage(img)
if _, appErr := a.Srv().Store.FileInfo().Upsert(fi); appErr != nil {
mlog.Error("creating mini preview failed", mlog.Err(appErr))
} else {
a.Srv().Store.FileInfo().InvalidateFileInfosForPostCache(fi.PostId, false)
}
}
}
func (a *App) generateMiniPreviewForInfos(fileInfos []*model.FileInfo) {
wg := new(sync.WaitGroup)
wg.Add(len(fileInfos))
for _, fileInfo := range fileInfos {
go func(fi *model.FileInfo) {
defer wg.Done()
a.generateMiniPreview(fi)
}(fileInfo)
}
wg.Wait()
}
func (a *App) GetFileInfo(fileId string) (*model.FileInfo, *model.AppError) { func (a *App) GetFileInfo(fileId string) (*model.FileInfo, *model.AppError) {
fileInfo, err := a.Srv().Store.FileInfo().Get(fileId) fileInfo, err := a.Srv().Store.FileInfo().Get(fileId)
if err != nil { if err != nil {
@@ -1137,6 +1178,7 @@ func (a *App) GetFileInfo(fileId string) (*model.FileInfo, *model.AppError) {
} }
} }
a.generateMiniPreview(fileInfo)
return fileInfo, nil return fileInfo, nil
} }
@@ -1155,6 +1197,8 @@ func (a *App) GetFileInfos(page, perPage int, opt *model.GetFileInfosOptions) ([
} }
} }
a.generateMiniPreviewForInfos(fileInfos)
return fileInfos, nil return fileInfos, nil
} }

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

@@ -1252,6 +1252,8 @@ func (a *App) GetFileInfosForPost(postId string, fromMaster bool) ([]*model.File
return nil, model.NewAppError("GetFileInfosForPost", "app.file_info.get_for_post.app_error", nil, err.Error(), http.StatusInternalServerError) return nil, model.NewAppError("GetFileInfosForPost", "app.file_info.get_for_post.app_error", nil, err.Error(), http.StatusInternalServerError)
} }
a.generateMiniPreviewForInfos(fileInfos)
return fileInfos, nil return fileInfos, nil
} }

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

@@ -4,9 +4,13 @@
package model package model
import ( import (
"bytes"
"encoding/json" "encoding/json"
"github.com/disintegration/imaging"
"github.com/mattermost/mattermost-server/v5/mlog"
"image" "image"
"image/gif" "image/gif"
"image/jpeg"
"io" "io"
"mime" "mime"
"net/http" "net/http"
@@ -36,22 +40,23 @@ type GetFileInfosOptions struct {
} }
type FileInfo struct { type FileInfo struct {
Id string `json:"id"` Id string `json:"id"`
CreatorId string `json:"user_id"` CreatorId string `json:"user_id"`
PostId string `json:"post_id,omitempty"` PostId string `json:"post_id,omitempty"`
CreateAt int64 `json:"create_at"` CreateAt int64 `json:"create_at"`
UpdateAt int64 `json:"update_at"` UpdateAt int64 `json:"update_at"`
DeleteAt int64 `json:"delete_at"` DeleteAt int64 `json:"delete_at"`
Path string `json:"-"` // not sent back to the client Path string `json:"-"` // not sent back to the client
ThumbnailPath string `json:"-"` // not sent back to the client ThumbnailPath string `json:"-"` // not sent back to the client
PreviewPath string `json:"-"` // not sent back to the client PreviewPath string `json:"-"` // not sent back to the client
Name string `json:"name"` Name string `json:"name"`
Extension string `json:"extension"` Extension string `json:"extension"`
Size int64 `json:"size"` Size int64 `json:"size"`
MimeType string `json:"mime_type"` MimeType string `json:"mime_type"`
Width int `json:"width,omitempty"` Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"` Height int `json:"height,omitempty"`
HasPreviewImage bool `json:"has_preview_image,omitempty"` HasPreviewImage bool `json:"has_preview_image,omitempty"`
MiniPreview *[]byte `json:"mini_preview"` // declared as *[]byte to avoid postgres/mysql differences in deserialization
} }
func (fi *FileInfo) ToJson() string { func (fi *FileInfo) ToJson() string {
@@ -150,6 +155,19 @@ func NewInfo(name string) *FileInfo {
return info return info
} }
func GenerateMiniPreviewImage(img image.Image) *[]byte {
preview := imaging.Resize(img, 16, 16, imaging.Lanczos)
buf := new(bytes.Buffer)
if err := jpeg.Encode(buf, preview, &jpeg.Options{Quality: 90}); err != nil {
mlog.Error("Unable to encode image as mini preview jpg", mlog.Err(err))
return nil
}
data := buf.Bytes()
return &data
}
func GetInfoForBytes(name string, data io.ReadSeeker, size int) (*FileInfo, *AppError) { func GetInfoForBytes(name string, data io.ReadSeeker, size int) (*FileInfo, *AppError) {
info := &FileInfo{ info := &FileInfo{
Name: name, Name: name,

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

@@ -3121,6 +3121,24 @@ func (s *OpenTracingLayerFileInfoStore) Save(info *model.FileInfo) (*model.FileI
return result, err return result, err
} }
func (s *OpenTracingLayerFileInfoStore) Upsert(info *model.FileInfo) (*model.FileInfo, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "FileInfoStore.Upsert")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.FileInfoStore.Upsert(info)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerGroupStore) AdminRoleGroupsForSyncableMember(userID string, syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) { func (s *OpenTracingLayerGroupStore) AdminRoleGroupsForSyncableMember(userID string, syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) {
origCtx := s.Root.Store.Context() origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.AdminRoleGroupsForSyncableMember") span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "GroupStore.AdminRoleGroupsForSyncableMember")

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

@@ -2916,6 +2916,26 @@ func (s *RetryLayerFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, e
} }
func (s *RetryLayerFileInfoStore) Upsert(info *model.FileInfo) (*model.FileInfo, error) {
tries := 0
for {
result, err := s.FileInfoStore.Upsert(info)
if err == nil {
return result, nil
}
if !isRepeatableError(err) {
return result, err
}
tries++
if tries >= 3 {
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
return result, err
}
}
}
func (s *RetryLayerGroupStore) AdminRoleGroupsForSyncableMember(userID string, syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) { func (s *RetryLayerGroupStore) AdminRoleGroupsForSyncableMember(userID string, syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) {
return s.GroupStore.AdminRoleGroupsForSyncableMember(userID, syncableID, syncableType) return s.GroupStore.AdminRoleGroupsForSyncableMember(userID, syncableID, syncableType)

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

@@ -64,6 +64,24 @@ func (fs SqlFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, error) {
return info, nil return info, nil
} }
func (fs SqlFileInfoStore) Upsert(info *model.FileInfo) (*model.FileInfo, error) {
info.PreSave()
if err := info.IsValid(); err != nil {
return nil, err
}
n, err := fs.GetMaster().Update(info)
if err != nil {
return nil, errors.Wrap(err, "failed to update FileInfo")
}
if n == 0 {
if err = fs.GetMaster().Insert(info); err != nil {
return nil, errors.Wrap(err, "failed to save FileInfo")
}
}
return info, nil
}
func (fs SqlFileInfoStore) Get(id string) (*model.FileInfo, error) { func (fs SqlFileInfoStore) Get(id string) (*model.FileInfo, error) {
info := &model.FileInfo{} info := &model.FileInfo{}

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

@@ -848,6 +848,7 @@ func upgradeDatabaseToVersion528(sqlStore SqlStore) {
sqlStore.AlterColumnTypeIfExists("Teams", "SchemeId", "VARCHAR(26)", "VARCHAR(26)") sqlStore.AlterColumnTypeIfExists("Teams", "SchemeId", "VARCHAR(26)", "VARCHAR(26)")
sqlStore.AlterColumnTypeIfExists("IncomingWebhooks", "Username", "varchar(255)", "varchar(255)") sqlStore.AlterColumnTypeIfExists("IncomingWebhooks", "Username", "varchar(255)", "varchar(255)")
sqlStore.AlterColumnTypeIfExists("IncomingWebhooks", "IconURL", "text", "varchar(1024)") sqlStore.AlterColumnTypeIfExists("IncomingWebhooks", "IconURL", "text", "varchar(1024)")
sqlStore.CreateColumnIfNotExistsNoDefault("FileInfo", "MiniPreview", "MEDIUMBLOB", "bytea")
saveSchemaVersion(sqlStore, VERSION_5_28_0) saveSchemaVersion(sqlStore, VERSION_5_28_0)
} }

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

@@ -548,6 +548,7 @@ type StatusStore interface {
type FileInfoStore interface { type FileInfoStore interface {
Save(info *model.FileInfo) (*model.FileInfo, error) Save(info *model.FileInfo) (*model.FileInfo, error)
Upsert(info *model.FileInfo) (*model.FileInfo, error)
Get(id string) (*model.FileInfo, error) Get(id string) (*model.FileInfo, error)
GetByPath(path string) (*model.FileInfo, error) GetByPath(path string) (*model.FileInfo, error)
GetForPost(postId string, readFromMaster, includeDeleted, allowFromCache bool) ([]*model.FileInfo, error) GetForPost(postId string, readFromMaster, includeDeleted, allowFromCache bool) ([]*model.FileInfo, error)

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

@@ -434,7 +434,7 @@ func testFileInfoAttachToPost(t *testing.T, ss store.Store) {
expected := []*model.FileInfo{info1, info2} expected := []*model.FileInfo{info1, info2}
sort.Sort(byFileInfoId(expected)) sort.Sort(byFileInfoId(expected))
sort.Sort(byFileInfoId(data)) sort.Sort(byFileInfoId(data))
assert.Equal(t, expected, data) assert.EqualValues(t, expected, data)
}) })
t.Run("should not attach files to multiple posts", func(t *testing.T) { t.Run("should not attach files to multiple posts", func(t *testing.T) {
@@ -488,7 +488,7 @@ func testFileInfoAttachToPost(t *testing.T, ss store.Store) {
data, err := ss.FileInfo().GetForPost(postId, true, false, false) data, err := ss.FileInfo().GetForPost(postId, true, false, false)
require.Nil(t, err) require.Nil(t, err)
info.PostId = postId info.PostId = postId
assert.Equal(t, []*model.FileInfo{info}, data) assert.EqualValues(t, []*model.FileInfo{info}, data)
}) })
} }

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

@@ -252,3 +252,26 @@ func (_m *FileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, error) {
return r0, r1 return r0, r1
} }
// Upsert provides a mock function with given fields: info
func (_m *FileInfoStore) Upsert(info *model.FileInfo) (*model.FileInfo, error) {
ret := _m.Called(info)
var r0 *model.FileInfo
if rf, ok := ret.Get(0).(func(*model.FileInfo) *model.FileInfo); ok {
r0 = rf(info)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.FileInfo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.FileInfo) error); ok {
r1 = rf(info)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

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

@@ -2861,6 +2861,22 @@ func (s *TimerLayerFileInfoStore) Save(info *model.FileInfo) (*model.FileInfo, e
return result, err return result, err
} }
func (s *TimerLayerFileInfoStore) Upsert(info *model.FileInfo) (*model.FileInfo, error) {
start := timemodule.Now()
result, err := s.FileInfoStore.Upsert(info)
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("FileInfoStore.Upsert", success, elapsed)
}
return result, err
}
func (s *TimerLayerGroupStore) AdminRoleGroupsForSyncableMember(userID string, syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) { func (s *TimerLayerGroupStore) AdminRoleGroupsForSyncableMember(userID string, syncableID string, syncableType model.GroupSyncableType) ([]string, *model.AppError) {
start := timemodule.Now() start := timemodule.Now()