Feature/fileinfo create copy (#9198)
* Initial implementation of a CopyFileInfos function that creates new FileInfo objects copied from provided FileIds with the provided user as the creator and not linked to a post yet. This can subsequently be used to copy existing attachments from another post to attach to a new post without having to re-upload the actual files * added a unit test for the CopyFileInfos function * resolving pull request suggestions
Этот коммит содержится в:
коммит произвёл
Harrison Healey
родитель
b728b16f90
Коммит
7a731d2bd1
30
app/file.go
30
app/file.go
@@ -604,3 +604,33 @@ func (a *App) GetFileInfo(fileId string) (*model.FileInfo, *model.AppError) {
|
||||
return result.Data.(*model.FileInfo), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) CopyFileInfos(userId string, fileIds []string) ([]string, *model.AppError) {
|
||||
newFileIds := []string{}
|
||||
|
||||
now := model.GetMillis()
|
||||
|
||||
for _, fileId := range fileIds {
|
||||
fileInfo := &model.FileInfo{}
|
||||
|
||||
if result := <-a.Srv.Store.FileInfo().Get(fileId); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
fileInfo = result.Data.(*model.FileInfo)
|
||||
}
|
||||
|
||||
fileInfo.Id = model.NewId()
|
||||
fileInfo.CreatorId = userId
|
||||
fileInfo.CreateAt = now
|
||||
fileInfo.UpdateAt = now
|
||||
fileInfo.PostId = ""
|
||||
|
||||
if result := <-a.Srv.Store.FileInfo().Save(fileInfo); result.Err != nil {
|
||||
return newFileIds, result.Err
|
||||
}
|
||||
|
||||
newFileIds = append(newFileIds, fileInfo.Id)
|
||||
}
|
||||
|
||||
return newFileIds, nil
|
||||
}
|
||||
|
||||
@@ -161,3 +161,34 @@ func TestMigrateFilenamesToFileInfos(t *testing.T) {
|
||||
infos = th.App.MigrateFilenamesToFileInfos(rpost)
|
||||
assert.Equal(t, 1, len(infos))
|
||||
}
|
||||
|
||||
func TestCopyFileInfos(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
teamId := model.NewId()
|
||||
channelId := model.NewId()
|
||||
userId := model.NewId()
|
||||
filename := "test"
|
||||
data := []byte("abcd")
|
||||
|
||||
info1, err := th.App.DoUploadFile(time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamId, channelId, userId, filename, data)
|
||||
require.Nil(t, err)
|
||||
defer func() {
|
||||
<-th.App.Srv.Store.FileInfo().PermanentDelete(info1.Id)
|
||||
th.App.RemoveFile(info1.Path)
|
||||
}()
|
||||
|
||||
infoIds, err := th.App.CopyFileInfos(userId, []string{info1.Id})
|
||||
require.Nil(t, err)
|
||||
|
||||
info2, err := th.App.GetFileInfo(infoIds[0])
|
||||
require.Nil(t, err)
|
||||
defer func() {
|
||||
<-th.App.Srv.Store.FileInfo().PermanentDelete(info2.Id)
|
||||
th.App.RemoveFile(info2.Path)
|
||||
}()
|
||||
|
||||
assert.NotEqual(t, info1.Id, info2.Id, "should not be equal")
|
||||
assert.Equal(t, info2.PostId, "", "should be empty string")
|
||||
}
|
||||
|
||||
@@ -275,6 +275,10 @@ func (api *PluginAPI) UpdatePost(post *model.Post) (*model.Post, *model.AppError
|
||||
return api.app.UpdatePost(post, false)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) CopyFileInfos(userId string, fileIds []string) ([]string, *model.AppError) {
|
||||
return api.app.CopyFileInfos(userId, fileIds)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) KVSet(key string, value []byte) *model.AppError {
|
||||
return api.app.SetPluginKey(api.id, key, value)
|
||||
}
|
||||
|
||||
@@ -155,6 +155,13 @@ type API interface {
|
||||
// UpdatePost updates a post.
|
||||
UpdatePost(post *model.Post) (*model.Post, *model.AppError)
|
||||
|
||||
// CopyFileInfos creates a copy of FileInfo objects provided in the list of FileIds
|
||||
// these new FileInfo objects will not be linked to any post and will
|
||||
// be ready to provide to a new CreatePost call
|
||||
// this should be used when you want to create a copy of a post including
|
||||
// file attachments without duplicating the file uploads
|
||||
CopyFileInfos(userId string, fileIds []string) ([]string, *model.AppError)
|
||||
|
||||
// KVSet will store a key-value pair, unique per plugin.
|
||||
KVSet(key string, value []byte) *model.AppError
|
||||
|
||||
|
||||
@@ -1820,6 +1820,36 @@ func (s *apiRPCServer) UpdatePost(args *Z_UpdatePostArgs, returns *Z_UpdatePostR
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_CopyFileInfosArgs struct {
|
||||
A string
|
||||
B []string
|
||||
}
|
||||
|
||||
type Z_CopyFileInfosReturns struct {
|
||||
A []string
|
||||
B *model.AppError
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) CopyFileInfos(userId string, fileIds []string) ([]string, *model.AppError) {
|
||||
_args := &Z_CopyFileInfosArgs{userId, fileIds}
|
||||
_returns := &Z_CopyFileInfosReturns{}
|
||||
if err := g.client.Call("Plugin.CopyFileInfos", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to CopyFileInfos API failed: %s", err.Error())
|
||||
}
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) CopyFileInfos(args *Z_CopyFileInfosArgs, returns *Z_CopyFileInfosReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
CopyFileInfos(userId string, fileIds []string) ([]string, *model.AppError)
|
||||
}); ok {
|
||||
returns.A, returns.B = hook.CopyFileInfos(args.A, args.B)
|
||||
} else {
|
||||
return fmt.Errorf("API CopyFileInfos called but not implemented.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_KVSetArgs struct {
|
||||
A string
|
||||
B []byte
|
||||
|
||||
@@ -37,6 +37,31 @@ func (_m *API) AddChannelMember(channelId string, userId string) (*model.Channel
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CopyFileInfos provides a mock function with given fields: userId, fileIds
|
||||
func (_m *API) CopyFileInfos(userId string, fileIds []string) ([]string, *model.AppError) {
|
||||
ret := _m.Called(userId, fileIds)
|
||||
|
||||
var r0 []string
|
||||
if rf, ok := ret.Get(0).(func(string, []string) []string); ok {
|
||||
r0 = rf(userId, fileIds)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]string)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 *model.AppError
|
||||
if rf, ok := ret.Get(1).(func(string, []string) *model.AppError); ok {
|
||||
r1 = rf(userId, fileIds)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*model.AppError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateChannel provides a mock function with given fields: channel
|
||||
func (_m *API) CreateChannel(channel *model.Channel) (*model.Channel, *model.AppError) {
|
||||
ret := _m.Called(channel)
|
||||
|
||||
Ссылка в новой задаче
Block a user