Skip to content

Commit

Permalink
Forward-porting fileinfo limits (mattermost-community#3164)
Browse files Browse the repository at this point in the history
* Updated go version

* Generated mocks

* lint fix

* lint fix

* lint fix

* backported fileinfo limits

* backported fileinfo limits

* added tests

* synced with main

* Server lint fix

* used a better name
  • Loading branch information
harshilsharma63 authored Jun 13, 2022
1 parent 04223a3 commit f3faf39
Show file tree
Hide file tree
Showing 31 changed files with 2,820 additions and 1,774 deletions.
25 changes: 25 additions & 0 deletions server/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -1913,6 +1913,31 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) {

w.Header().Set("Content-Type", contentType)

fileInfo, err := a.app.GetFileInfo(filename)
if err != nil {
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
return
}

if fileInfo != nil && fileInfo.Archived {
fileMetadata := map[string]interface{}{
"archived": true,
"name": fileInfo.Name,
"size": fileInfo.Size,
"extension": fileInfo.Extension,
}

data, jsonErr := json.Marshal(fileMetadata)
if jsonErr != nil {
a.logger.Error("failed to marshal archived file metadata", mlog.String("filename", filename), mlog.Err(jsonErr))
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", jsonErr)
return
}

jsonBytesResponse(w, http.StatusBadRequest, data)
return
}

fileReader, err := a.app.GetFileReader(board.TeamID, boardID, filename)
if err != nil {
a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err)
Expand Down
67 changes: 60 additions & 7 deletions server/app/files.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,87 @@
package app

import (
"errors"
"fmt"
"io"
"path/filepath"
"strings"

"github.com/mattermost/focalboard/server/utils"

"github.com/mattermost/mattermost-server/v6/shared/mlog"
mmModel "github.com/mattermost/mattermost-server/v6/model"

"github.com/mattermost/focalboard/server/utils"
"github.com/mattermost/mattermost-server/v6/shared/filestore"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
)

const emptyString = "empty"

var errEmptyFilename = errors.New("IsFileArchived: empty filename not allowed")

func (a *App) SaveFile(reader io.Reader, teamID, rootID, filename string) (string, error) {
// NOTE: File extension includes the dot
fileExtension := strings.ToLower(filepath.Ext(filename))
if fileExtension == ".jpeg" {
fileExtension = ".jpg"
}

createdFilename := fmt.Sprintf(`%s%s`, utils.NewID(utils.IDTypeNone), fileExtension)
filePath := filepath.Join(teamID, rootID, createdFilename)
createdFilename := utils.NewID(utils.IDTypeNone)
fullFilename := fmt.Sprintf(`%s%s`, createdFilename, fileExtension)
filePath := filepath.Join(teamID, rootID, fullFilename)

_, appErr := a.filesBackend.WriteFile(reader, filePath)
fileSize, appErr := a.filesBackend.WriteFile(reader, filePath)
if appErr != nil {
return "", fmt.Errorf("unable to store the file in the files storage: %w", appErr)
}

return createdFilename, nil
now := utils.GetMillis()

fileInfo := &mmModel.FileInfo{
Id: createdFilename[1:],
CreatorId: "boards",
PostId: emptyString,
ChannelId: emptyString,
CreateAt: now,
UpdateAt: now,
DeleteAt: 0,
Path: emptyString,
ThumbnailPath: emptyString,
PreviewPath: emptyString,
Name: filename,
Extension: fileExtension,
Size: fileSize,
MimeType: emptyString,
Width: 0,
Height: 0,
HasPreviewImage: false,
MiniPreview: nil,
Content: "",
RemoteId: nil,
}
err := a.store.SaveFileInfo(fileInfo)
if err != nil {
return "", err
}

return fullFilename, nil
}

func (a *App) GetFileInfo(filename string) (*mmModel.FileInfo, error) {
if len(filename) == 0 {
return nil, errEmptyFilename
}

// filename is in the format 7<some-alphanumeric-string>.<extension>
// we want to extract the <some-alphanumeric-string> part of this as this
// will be the fileinfo id.
parts := strings.Split(filename, ".")
fileInfoID := parts[0][1:]
fileInfo, err := a.store.GetFileInfo(fileInfoID)
if err != nil {
return nil, err
}

return fileInfo, nil
}

func (a *App) GetFileReader(teamID, rootID, filename string) (filestore.ReadCloseSeeker, error) {
Expand Down
53 changes: 53 additions & 0 deletions server/app/files_test.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package app

import (
"errors"
"io"
"os"
"path/filepath"
"strings"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"

mmModel "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin/plugintest/mock"
"github.com/mattermost/mattermost-server/v6/shared/filestore"
"github.com/mattermost/mattermost-server/v6/shared/filestore/mocks"
Expand All @@ -19,6 +22,8 @@ const (
testBoardID = "test-board-id"
)

var errDummy = errors.New("hello")

type TestError struct{}

func (err *TestError) Error() string { return "Mocked File backend error" }
Expand Down Expand Up @@ -186,6 +191,7 @@ func TestSaveFile(t *testing.T) {
fileName := "temp-file-name.txt"
mockedFileBackend := &mocks.FileBackend{}
th.App.filesBackend = mockedFileBackend
th.Store.EXPECT().SaveFileInfo(gomock.Any()).Return(nil)

writeFileFunc := func(reader io.Reader, path string) int64 {
paths := strings.Split(path, string(os.PathSeparator))
Expand All @@ -209,6 +215,7 @@ func TestSaveFile(t *testing.T) {
fileName := "temp-file-name.jpeg"
mockedFileBackend := &mocks.FileBackend{}
th.App.filesBackend = mockedFileBackend
th.Store.EXPECT().SaveFileInfo(gomock.Any()).Return(nil)

writeFileFunc := func(reader io.Reader, path string) int64 {
paths := strings.Split(path, string(os.PathSeparator))
Expand All @@ -233,6 +240,7 @@ func TestSaveFile(t *testing.T) {
mockedFileBackend := &mocks.FileBackend{}
th.App.filesBackend = mockedFileBackend
mockedError := &TestError{}
th.Store.EXPECT().SaveFileInfo(gomock.Any()).Return(nil)

writeFileFunc := func(reader io.Reader, path string) int64 {
paths := strings.Split(path, string(os.PathSeparator))
Expand All @@ -252,3 +260,48 @@ func TestSaveFile(t *testing.T) {
assert.Equal(t, "unable to store the file in the files storage: Mocked File backend error", err.Error())
})
}

func TestGetFileInfo(t *testing.T) {
th, _ := SetupTestHelper(t)

t.Run("should return file info", func(t *testing.T) {
fileInfo := &mmModel.FileInfo{
Id: "file_info_id",
Archived: false,
}

th.Store.EXPECT().GetFileInfo("filename").Return(fileInfo, nil).Times(2)

fetchedFileInfo, err := th.App.GetFileInfo("Afilename")
assert.NoError(t, err)
assert.Equal(t, "file_info_id", fetchedFileInfo.Id)
assert.False(t, fetchedFileInfo.Archived)

fetchedFileInfo, err = th.App.GetFileInfo("Afilename.txt")
assert.NoError(t, err)
assert.Equal(t, "file_info_id", fetchedFileInfo.Id)
assert.False(t, fetchedFileInfo.Archived)
})

t.Run("should return archived file info", func(t *testing.T) {
fileInfo := &mmModel.FileInfo{
Id: "file_info_id",
Archived: true,
}

th.Store.EXPECT().GetFileInfo("filename").Return(fileInfo, nil)

fetchedFileInfo, err := th.App.GetFileInfo("Afilename")
assert.NoError(t, err)
assert.Equal(t, "file_info_id", fetchedFileInfo.Id)
assert.True(t, fetchedFileInfo.Archived)
})

t.Run("should return archived file infoerror", func(t *testing.T) {
th.Store.EXPECT().GetFileInfo("filename").Return(nil, errDummy)

fetchedFileInfo, err := th.App.GetFileInfo("Afilename")
assert.Error(t, err)
assert.Nil(t, fetchedFileInfo)
})
}
79 changes: 79 additions & 0 deletions server/services/store/mattermostauthlayer/mattermostauthlayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package mattermostauthlayer
import (
"database/sql"
"encoding/json"
"net/http"

mmModel "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
Expand Down Expand Up @@ -383,6 +384,84 @@ func mmUserToFbUser(mmUser *mmModel.User) model.User {
}
}

func (s *MattermostAuthLayer) GetFileInfo(id string) (*mmModel.FileInfo, error) {
fileInfo, appErr := s.pluginAPI.GetFileInfo(id)
if appErr != nil {
// Not finding fileinfo is fine because we don't have data for
// any existing files already uploaded in Boards before this code
// was deployed.
if appErr.StatusCode == http.StatusNotFound {
return nil, nil
}

s.logger.Error("error fetching fileinfo", mlog.String("id", id), mlog.Err(appErr))
return nil, appErr
}

return fileInfo, nil
}

func (s *MattermostAuthLayer) SaveFileInfo(fileInfo *mmModel.FileInfo) error {
query := s.getQueryBuilder().
Insert("FileInfo").
Columns(
"Id",
"CreatorId",
"PostId",
"CreateAt",
"UpdateAt",
"DeleteAt",
"Path",
"ThumbnailPath",
"PreviewPath",
"Name",
"Extension",
"Size",
"MimeType",
"Width",
"Height",
"HasPreviewImage",
"MiniPreview",
"Content",
"RemoteId",
"Archived",
).
Values(
fileInfo.Id,
fileInfo.CreatorId,
fileInfo.PostId,
fileInfo.CreateAt,
fileInfo.UpdateAt,
fileInfo.DeleteAt,
fileInfo.Path,
fileInfo.ThumbnailPath,
fileInfo.PreviewPath,
fileInfo.Name,
fileInfo.Extension,
fileInfo.Size,
fileInfo.MimeType,
fileInfo.Width,
fileInfo.Height,
fileInfo.HasPreviewImage,
fileInfo.MiniPreview,
fileInfo.Content,
fileInfo.RemoteId,
false,
)

if _, err := query.Exec(); err != nil {
s.logger.Error(
"failed to save fileinfo",
mlog.String("file_name", fileInfo.Name),
mlog.Int64("size", fileInfo.Size),
mlog.Err(err),
)
return err
}

return nil
}

func (s *MattermostAuthLayer) GetLicense() *mmModel.License {
return s.pluginAPI.GetLicense()
}
29 changes: 29 additions & 0 deletions server/services/store/mockstore/mockstore.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f3faf39

Please sign in to comment.