* Updated patch/update post API to allow file modification (#29447)

* WIP

* WIP

* Atatched new files ton post

* WIP: deleting removed files

* Deleted removed files and invalidated file metadata cache

* removed file ignore logif from update post API

* Added TestFindExclusives

* Added tests for DeleteForPostByIds

* Added app layer tests

* Added tests

* Added API level tests

* test enhancements

* Fixed a test

* Edit history include file metadata (#29505)

* Send file metadata in edit history metadata

* Added app tests

* Added store tests

* Added tests for populateEditHistoryFileMetadata{

* Added cache to avoid repetitigve DB calls for edits with only message changes

* Added API tests

* i18m fix

* removed commented code

* Improved test helper

* Show attachments in edit history RHS (#29519)

* Send file metadata in edit history metadata

* Added app tests

* Added store tests

* Added tests for populateEditHistoryFileMetadata{

* Added cache to avoid repetitigve DB calls for edits with only message changes

* Added API tests

* i18m fix

* WIUP: displa files in edit

* removed commented code

* Displayed file in edit history

* Handled file icon

* Fixed closing history component on clicking on file

* Simplified selector

* Simplified selector

* Improved test helper

* Disabled action menu on edit history file

* Added tests

* Improved selector

* Updated snapshot

* review Fixes

* restructured componnets

* Updated test

* Updated test

* Restore post api (#29643)

* Restore post version API WIP

* Undelete files WIP

* Added store tests

* Created post restore API

* Updated updatepost safeUpdate signature

* review fixex and improvements

* Fixed an app test

* Added API laer tests

* Added API tests and OpenAPI specs

* Fixed a typo

* Allow editing files when editing posts (#29709)

* WIP - basic view files when editing post

* Cleanup

* bg color

* Added text editor tests for files

* WIP

* WIP

* removed debug log

* Allowed admin to add and remove files on someone else's post

* Handled drafts and scheduled posts

* linter fixes

* Updated snapshot

* server test fix

* CI

* Added doc

* Restore post api integration (#29719)

* WIP - basic view files when editing post

* Cleanup

* bg color

* Added text editor tests for files

* WIP

* WIP

* removed debug log

* Allowed admin to add and remove files on someone else's post

* Handled drafts and scheduled posts

* linter fixes

* Updated snapshot

* server test fix

* Used new API to restore post

* handled edut limit and undo

* lint fix

* added comments

* Fixed edit post item tests

* Fixed buttons

* Aded snapshots

* fix test

* Updated snapshot

* Minor fixes

* fixed snapshot

* Edit file dnd area (#29763)

* dnd wip

* DND continued

* Supported multiple unbind dragster funcs

* lint fixes

* Got center channel file drop working when editing a post

* file dnd working with center channel and rhs

* file dnd working with center channel and rhs

* removed unneeded stopPropogation calls

* cleanup

* DND overlay fix

* Lint fix

* Advanced text editor test updates for file upload overlay

* fixed use upload hook tests

* Updated some more snapshots

* minor cleanup

* Updated i18n

* removed need of array for dragster unbind events

* lint fixes

* edit history cursor

* Fixed bugu causing faliure to delete empty posts (#29778)

* Files in restore confirmation (#29781)

* Added files to restore post confirmation dialog

* Fixed post restore toast colors

* Fixed restore bug

* Fixed restore confirmation toast tests

* a11y improvement and modal width fix

* Edit attachment misc fixes (#29808)

* Removed single image actions in restore post confirmation dialog

* Fixed file drop overlay size and position

* Made edit indiator accessible

* Lint fix

* Added bunch of more tests

* ANother test migrated from enzyme to react testing library

* More test enhancements

* More test enhancements

* More test enhancements

* lint fixes

* Fixed  a test

* Added missing snapshots

* Test fixes
Этот коммит содержится в:
Harshil Sharma
2025-01-13 18:16:56 +05:30
коммит произвёл GitHub
родитель ecdce71fc4
Коммит 6e5a67caec
91 изменённых файлов: 4945 добавлений и 1284 удалений

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

@@ -269,7 +269,7 @@ func TestUpdatePostEditAt(t *testing.T) {
post := th.BasicPost.Clone()
post.IsPinned = true
saved, err := th.App.UpdatePost(th.Context, post, true)
saved, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err)
assert.Equal(t, saved.EditAt, post.EditAt, "shouldn't have updated post.EditAt when pinning post")
post = saved.Clone()
@@ -277,7 +277,7 @@ func TestUpdatePostEditAt(t *testing.T) {
time.Sleep(time.Millisecond * 100)
post.Message = model.NewId()
saved, err = th.App.UpdatePost(th.Context, post, true)
saved, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err)
assert.NotEqual(t, saved.EditAt, post.EditAt, "should have updated post.EditAt when updating post message")
@@ -295,7 +295,7 @@ func TestUpdatePostTimeLimit(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.PostEditTimeLimit = -1
})
_, err := th.App.UpdatePost(th.Context, post, true)
_, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err)
th.App.UpdateConfig(func(cfg *model.Config) {
@@ -303,14 +303,14 @@ func TestUpdatePostTimeLimit(t *testing.T) {
})
post.Message = model.NewId()
_, err = th.App.UpdatePost(th.Context, post, true)
_, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err, "should allow you to edit the post")
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.PostEditTimeLimit = 1
})
post.Message = model.NewId()
_, err = th.App.UpdatePost(th.Context, post, true)
_, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err, "should allow you to edit an old post because the time check is applied above in the call hierarchy")
th.App.UpdateConfig(func(cfg *model.Config) {
@@ -326,7 +326,7 @@ func TestUpdatePostInArchivedChannel(t *testing.T) {
post := th.CreatePost(archivedChannel)
th.App.DeleteChannel(th.Context, archivedChannel, "")
_, err := th.App.UpdatePost(th.Context, post, true)
_, err := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.NotNil(t, err)
require.Equal(t, "api.post.update_post.can_not_update_post_in_deleted.error", err.Id)
}
@@ -472,7 +472,7 @@ func TestUpdatePostPluginHooks(t *testing.T) {
require.Nil(t, err)
post.Message = "new message"
updatedPost, err := th.App.UpdatePost(th.Context, post, false)
updatedPost, err := th.App.UpdatePost(th.Context, post, nil)
require.Nil(t, updatedPost)
require.NotNil(t, err)
require.Equal(t, "Post rejected by plugin. rejected", err.Id)
@@ -539,7 +539,7 @@ func TestUpdatePostPluginHooks(t *testing.T) {
require.Nil(t, err)
post.Message = "new message"
updatedPost, err := th.App.UpdatePost(th.Context, post, false)
updatedPost, err := th.App.UpdatePost(th.Context, post, nil)
require.Nil(t, err)
require.NotNil(t, updatedPost)
require.Equal(t, "2 new message 1", updatedPost.Message)
@@ -591,7 +591,7 @@ func TestPostChannelMentions(t *testing.T) {
}, post.GetProp("channel_mentions"))
post.Message = fmt.Sprintf("goodbye, ~%v!", channelToMention2.Name)
result, err := th.App.UpdatePost(th.Context, post, false)
result, err := th.App.UpdatePost(th.Context, post, nil)
require.Nil(t, err)
assert.Equal(t, map[string]any{
"mention-test2": map[string]any{
@@ -601,7 +601,7 @@ func TestPostChannelMentions(t *testing.T) {
}, result.GetProp("channel_mentions"))
result.Message = "no more mentions!"
result, err = th.App.UpdatePost(th.Context, result, false)
result, err = th.App.UpdatePost(th.Context, result, nil)
require.Nil(t, err)
assert.Nil(t, result.GetProp("channel_mentions"))
}
@@ -1123,7 +1123,7 @@ func TestCreatePost(t *testing.T) {
go func() {
defer wg.Done()
post := previewPost.Clone()
th.App.UpdatePost(th.Context, post, false)
th.App.UpdatePost(th.Context, post, nil)
}()
}
@@ -1194,7 +1194,7 @@ func TestPatchPost(t *testing.T) {
Message: model.NewPointer("![image](" + imageURL + ")"),
}
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patch)
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patch, nil)
require.Nil(t, err)
assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
})
@@ -1217,13 +1217,13 @@ func TestPatchPost(t *testing.T) {
t.Run("Does not set prop when user has USE_CHANNEL_MENTIONS", func(t *testing.T) {
patchWithNoMention := &model.PostPatch{Message: model.NewPointer("This patch has no channel mention")}
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention)
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil)
require.Nil(t, err)
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
patchWithMention := &model.PostPatch{Message: model.NewPointer("This patch has a mention now @here")}
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention)
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil)
require.Nil(t, err)
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
})
@@ -1233,13 +1233,13 @@ func TestPatchPost(t *testing.T) {
th.RemovePermissionFromRole(model.PermissionUseChannelMentions.Id, model.ChannelAdminRoleId)
patchWithNoMention := &model.PostPatch{Message: model.NewPointer("This patch still does not have a mention")}
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention)
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithNoMention, nil)
require.Nil(t, err)
assert.Equal(t, rpost.GetProps(), model.StringInterface{})
patchWithMention := &model.PostPatch{Message: model.NewPointer("This patch has a mention now @here")}
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention)
rpost, err = th.App.PatchPost(th.Context, rpost.Id, patchWithMention, nil)
require.Nil(t, err)
assert.Equal(t, rpost.GetProp(model.PostPropsMentionHighlightDisabled), true)
@@ -1433,7 +1433,7 @@ func TestPatchPostInArchivedChannel(t *testing.T) {
post := th.CreatePost(archivedChannel)
th.App.DeleteChannel(th.Context, archivedChannel, "")
_, err := th.App.PatchPost(th.Context, post.Id, &model.PostPatch{IsPinned: model.NewPointer(true)})
_, err := th.App.PatchPost(th.Context, post.Id, &model.PostPatch{IsPinned: model.NewPointer(true)}, nil)
require.NotNil(t, err)
require.Equal(t, "api.post.patch_post.can_not_update_post_in_deleted.error", err.Id)
}
@@ -1539,7 +1539,7 @@ func TestUpdatePost(t *testing.T) {
post.Id = rpost.Id
post.Message = "![image](" + imageURL + ")"
rpost, err = th.App.UpdatePost(th.Context, post, false)
rpost, err = th.App.UpdatePost(th.Context, post, nil)
require.Nil(t, err)
assert.Equal(t, "![image]("+proxiedImageURL+")", rpost.Message)
})
@@ -1578,7 +1578,7 @@ func TestUpdatePost(t *testing.T) {
assert.Equal(t, model.StringInterface{}, testPost.GetProps())
testPost.Message = permalink
testPost, err = th.App.UpdatePost(th.Context, testPost, false)
testPost, err = th.App.UpdatePost(th.Context, testPost, nil)
require.Nil(t, err)
assert.Equal(t, model.StringInterface{model.PostPropsPreviewedPost: referencedPost.Id}, testPost.GetProps())
})
@@ -1639,7 +1639,7 @@ func TestUpdatePost(t *testing.T) {
permalink := fmt.Sprintf("%s/%s/pl/%s", *th.App.Config().ServiceSettings.SiteURL, th.BasicTeam.Name, referencedPost.Id)
previewPost.Message = permalink
previewPost, err = th.App.UpdatePost(th.Context, previewPost, false)
previewPost, err = th.App.UpdatePost(th.Context, previewPost, nil)
require.Nil(t, err)
require.Len(t, previewPost.Metadata.Embeds, testCase.Length)
@@ -3126,7 +3126,7 @@ func TestSharedChannelSyncForPostActions(t *testing.T) {
}, channel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err, "Creating a post should not error")
_, err = th.App.UpdatePost(th.Context, post, true)
_, err = th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{SafeUpdate: true})
require.Nil(t, err, "Updating a post should not error")
require.Len(t, sharedChannelService.channelNotifications, 2)
@@ -3410,7 +3410,6 @@ func TestComputeLastAccessiblePostTime(t *testing.T) {
}
func TestGetEditHistoryForPost(t *testing.T) {
t.Skip("This needs fixing, OriginalId seems to be empty for all posts")
th := Setup(t).InitBasic()
defer th.TearDown()
@@ -3427,7 +3426,7 @@ func TestGetEditHistoryForPost(t *testing.T) {
patch := &model.PostPatch{
Message: model.NewPointer("new message edited"),
}
_, err1 := th.App.PatchPost(th.Context, rpost.Id, patch)
_, err1 := th.App.PatchPost(th.Context, rpost.Id, patch, nil)
require.Nil(t, err1)
// update the post message again
@@ -3435,14 +3434,13 @@ func TestGetEditHistoryForPost(t *testing.T) {
Message: model.NewPointer("new message edited again"),
}
_, err2 := th.App.PatchPost(th.Context, rpost.Id, patch)
_, err2 := th.App.PatchPost(th.Context, rpost.Id, patch, nil)
require.Nil(t, err2)
// get the edit history
edits, err := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, err)
t.Run("should return the edit history", func(t *testing.T) {
edits, err := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, err)
require.Len(t, edits, 2)
require.Equal(t, "new message edited", edits[0].Message)
require.Equal(t, "new message", edits[1].Message)
@@ -3453,6 +3451,103 @@ func TestGetEditHistoryForPost(t *testing.T) {
require.NotNil(t, err)
require.Empty(t, edits)
})
t.Run("edit history should contain file metadata", func(t *testing.T) {
fileBytes := []byte("file contents")
fileInfo, err := th.App.UploadFile(th.Context, fileBytes, th.BasicChannel.Id, "file.txt")
require.Nil(t, err)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "new message",
UserId: th.BasicUser.Id,
FileIds: model.StringArray{fileInfo.Id},
}
_, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, err)
patch := &model.PostPatch{
Message: model.NewPointer("new message edited"),
}
_, appErr := th.App.PatchPost(th.Context, post.Id, patch, nil)
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 2"),
}
_, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 3"),
}
_, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
require.Nil(t, appErr)
edits, err := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, err)
require.Len(t, edits, 3)
for _, edit := range edits {
require.Len(t, edit.FileIds, 1)
require.Equal(t, fileInfo.Id, edit.FileIds[0])
require.Len(t, edit.Metadata.Files, 1)
require.Equal(t, fileInfo.Id, edit.Metadata.Files[0].Id)
}
})
t.Run("edit history should contain file metadata even if the file info is deleted", func(t *testing.T) {
fileBytes := []byte("file contents")
fileInfo, appErr := th.App.UploadFile(th.Context, fileBytes, th.BasicChannel.Id, "file.txt")
require.Nil(t, appErr)
post := &model.Post{
ChannelId: th.BasicChannel.Id,
Message: "new message",
UserId: th.BasicUser.Id,
FileIds: model.StringArray{fileInfo.Id},
}
_, appErr = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true})
require.Nil(t, appErr)
patch := &model.PostPatch{
Message: model.NewPointer("new message edited"),
}
_, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 2"),
}
_, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
require.Nil(t, appErr)
patch = &model.PostPatch{
Message: model.NewPointer("new message edited 3"),
}
_, appErr = th.App.PatchPost(th.Context, post.Id, patch, nil)
require.Nil(t, appErr)
// now delete the file info, and it should still be include in edit history metadata
_, err := th.App.Srv().Store().FileInfo().DeleteForPost(th.Context, post.Id)
require.NoError(t, err)
edits, appErr := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, appErr)
require.Len(t, edits, 3)
for _, edit := range edits {
require.Len(t, edit.FileIds, 1)
require.Equal(t, fileInfo.Id, edit.FileIds[0])
require.Len(t, edit.Metadata.Files, 1)
require.Equal(t, fileInfo.Id, edit.Metadata.Files[0].Id)
require.Greater(t, edit.Metadata.Files[0].DeleteAt, int64(0))
}
})
}
func TestCopyWranglerPostlist(t *testing.T) {
@@ -3731,3 +3826,121 @@ func TestSendTestMessage(t *testing.T) {
assert.NotEmpty(t, post.GetProp(model.PostPropsForceNotification))
})
}
func TestPopulateEditHistoryFileMetadata(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
t.Run("should populate file metadata for all posts", func(t *testing.T) {
fileInfo1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
fileInfo2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post1 := th.CreatePost(th.BasicChannel, func(post *model.Post) {
post.FileIds = model.StringArray{fileInfo1.Id}
})
post2 := th.CreatePost(th.BasicChannel, func(post *model.Post) {
post.FileIds = model.StringArray{fileInfo2.Id}
})
appErr := th.App.populateEditHistoryFileMetadata([]*model.Post{post1, post2})
require.Nil(t, appErr)
require.Len(t, post1.Metadata.Files, 1)
require.Equal(t, fileInfo1.Id, post1.Metadata.Files[0].Id)
require.Len(t, post2.Metadata.Files, 1)
require.Equal(t, fileInfo2.Id, post2.Metadata.Files[0].Id)
})
t.Run("should populate file metadata even for deleted posts", func(t *testing.T) {
fileInfo1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
fileInfo2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post1 := th.CreatePost(th.BasicChannel, func(post *model.Post) {
post.FileIds = model.StringArray{fileInfo1.Id}
})
post2 := th.CreatePost(th.BasicChannel, func(post *model.Post) {
post.FileIds = model.StringArray{fileInfo2.Id}
})
_, appErr := th.App.DeletePost(th.Context, post1.Id, th.BasicUser.Id)
require.Nil(t, appErr)
_, appErr = th.App.DeletePost(th.Context, post2.Id, th.BasicUser.Id)
require.Nil(t, appErr)
appErr = th.App.populateEditHistoryFileMetadata([]*model.Post{post1, post2})
require.Nil(t, appErr)
require.Len(t, post1.Metadata.Files, 1)
require.Equal(t, fileInfo1.Id, post1.Metadata.Files[0].Id)
require.Len(t, post2.Metadata.Files, 1)
require.Equal(t, fileInfo2.Id, post2.Metadata.Files[0].Id)
})
t.Run("should populate file metadata even for deleted fileInfos", func(t *testing.T) {
fileInfo1, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
fileInfo2, err := th.App.Srv().Store().FileInfo().Save(th.Context,
&model.FileInfo{
CreatorId: th.BasicUser.Id,
Path: "path.txt",
})
require.NoError(t, err)
post1 := th.CreatePost(th.BasicChannel, func(post *model.Post) {
post.FileIds = model.StringArray{fileInfo1.Id}
})
post2 := th.CreatePost(th.BasicChannel, func(post *model.Post) {
post.FileIds = model.StringArray{fileInfo2.Id}
})
_, err = th.App.Srv().Store().FileInfo().DeleteForPost(th.Context, post1.Id)
require.NoError(t, err)
_, err = th.App.Srv().Store().FileInfo().DeleteForPost(th.Context, post2.Id)
require.NoError(t, err)
appErr := th.App.populateEditHistoryFileMetadata([]*model.Post{post1, post2})
require.Nil(t, appErr)
require.Len(t, post1.Metadata.Files, 1)
require.Equal(t, fileInfo1.Id, post1.Metadata.Files[0].Id)
require.Greater(t, post1.Metadata.Files[0].DeleteAt, int64(0))
require.Len(t, post2.Metadata.Files, 1)
require.Equal(t, fileInfo2.Id, post2.Metadata.Files[0].Id)
require.Greater(t, post2.Metadata.Files[0].DeleteAt, int64(0))
})
}