Channel Bookmarks (#25449)
* create ChannelBookmarks table * ChannelBookmark model * channel bookamrks Store layer * add GetBookmarksForAllChannelByIdSince * add channel bookmarks to test store * Add channel bookmarks to app layer * remove index for createAt in channel bookmarks migrations * remove createAt from select channel bookmark query and enable store delete bookmark test * update reponse of UpdateBookmark * rename db migration files * channel bookmarks store update sort order * channel bookmarks app layer update sort order * fix lint & tests * Fix lint and introduce util functions to insert / remove from slice * remove model etag * i18n * defer remove file info after test run * Fix tests passing the request context * fix migrations * fix TestRetry * Add bookmark permissions (#25560) * Adds channel bookmarks permissions * Fix linter * Remove unnecessary empty lines * Remove scss change as it's not necessary anymore * Fix mock store * Fix mock store and add role entry * Fix test * Adds cypress test and update permissions migration to update admin roles * Adds channel bookmarks roles to default admin roles * Adds bookmark permissions to default role permissions constant in webapp * Update mmctl test * Update permission test after normalising the roles * fix store tests * fix app layer tests * Add new bookmark endpoint (#25624) * Adds channel bookmarks api scaffold and create endpoint * Applies review comments to the API docs * Adds websocket test to create channel bookmark --------- Co-authored-by: Mattermost Build <build@mattermost.com> * MM-54426 exclude Channel Bookmarks files from data retention (#25656) * Augment channel APIs to include bookmarks (#25567) * update files docs for server 9.4 * Adds update channel bookmark endpoint (#25653) * Adds update channel bookmark sort order endpoint (#25686) * Adds update channel bookmark endpoint * Updates edit app method to return the right deleted bookmark and adds tests * Adds the update channel bookmark sort order endpoint * Fix repeated test after merge * Assign right permissions to each test * Update store and app layer to return specific errors and add tests * Adds delete channel bookmark endpoint (#25693) * Updates edit app method to return the right deleted bookmark and adds tests * Fix repeated test after merge * Updates edit app method to return the right deleted bookmark and adds tests * Adds delete channel bookmark endpoint * Adds list channel bookmarks endpoint (#25700) * Add channel moderation to bookmarks (#25716) * fix migrations index * fix getChannelsForTeamForUser * fix getChannelsForTeamForUser * fix bad merge client4 * fix file api with bookmark permission * add ChannelBookmarks feature flag * add missing translations * Set DB column for type as enum * use custom type for bookmark query using sqlx * use transaction when saving bookmark * return NewErrNotFound instead of Sql.ErrNoRows * use squirrel for IN query * add a limit of 1K for records in GetBookmarksForAllChannelByIdSince * UpdateSortOrder with one single query instead of multiple updates * fix shadow declaration * fix channel bookmarks permission string definition in admin console * fix another shadow declaration * Fix model conversion * add SplitSliceInChunks * remove include bookmarks in channels api * Cap amount of bookmarks per channel * add etag back to get channels * feedback review * update file info when replacing a bookmark file * return 501 not implemented when the license is not available * add detail message when getting channel member on bookmark api * start audit before permission check on create bookmark api * use require.Eventuallyf for testing WS events * remove unnecessary log in app layer * use require instead of assert to avoid panics * enforce limit when querying bookmarks since * prevent to create/update bookmark if file is already attached * fix lint * delete file when a bookmark is deleted * Dot allow to set a fileId and a url at the same time to a bookmark * fix query to delete a file that belongs to a bookmark * do not patch the bookmark type * Server side FeatureFlag check (#26145) * use ff in server, set ff to false * turn on FF for unit tests * defer unset FF for unit tests * turn ff on for testing * only allow attaching files that were uploaded for bookmark * Set feature flag off as default * fix lint * update email templates as PR failed * revert templates * force the assignment of ID when creating a bookmark * Fix unit tests --------- Co-authored-by: Miguel de la Cruz <miguel@mcrx.me> Co-authored-by: Mattermost Build <build@mattermost.com> Co-authored-by: Caleb Roseland <caleb@calebroseland.com> Co-authored-by: Scott Bishel <scott.bishel@mattermost.com>
Этот коммит содержится в:
483
server/channels/store/storetest/channel_bookmark.go
Обычный файл
483
server/channels/store/storetest/channel_bookmark.go
Обычный файл
@@ -0,0 +1,483 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package storetest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/request"
|
||||
"github.com/mattermost/mattermost/server/v8/channels/store"
|
||||
)
|
||||
|
||||
func find_bookmark(slice []*model.ChannelBookmarkWithFileInfo, id string) *model.ChannelBookmarkWithFileInfo {
|
||||
for _, element := range slice {
|
||||
if element.Id == id {
|
||||
return element
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestChannelBookmarkStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
||||
t.Run("SaveChannelBookmark", func(t *testing.T) { testSaveChannelBookmark(t, rctx, ss) })
|
||||
t.Run("UpdateChannelBookmark", func(t *testing.T) { testUpdateChannelBookmark(t, rctx, ss) })
|
||||
t.Run("UpdateSortOrderChannelBookmark", func(t *testing.T) { testUpdateSortOrderChannelBookmark(t, rctx, ss) })
|
||||
t.Run("DeleteChannelBookmark", func(t *testing.T) { testDeleteChannelBookmark(t, rctx, ss) })
|
||||
t.Run("GetChannelBookmark", func(t *testing.T) { testGetChannelBookmark(t, rctx, ss) })
|
||||
}
|
||||
|
||||
func testSaveChannelBookmark(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
channelId := model.NewId()
|
||||
userId := model.NewId()
|
||||
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
file := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
bookmark2 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "file bookmark test",
|
||||
FileId: file.Id,
|
||||
Type: model.ChannelBookmarkFile,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
bookmark3 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "file already attached",
|
||||
FileId: file.Id,
|
||||
Type: model.ChannelBookmarkFile,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
file2 := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: userId,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
bookmark4 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "file already attached to a post",
|
||||
FileId: file2.Id,
|
||||
Type: model.ChannelBookmarkFile,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
_, err := ss.FileInfo().Save(rctx, file)
|
||||
require.NoError(t, err)
|
||||
defer ss.FileInfo().PermanentDelete(rctx, file.Id)
|
||||
|
||||
_, err = ss.FileInfo().Save(rctx, file2)
|
||||
require.NoError(t, err)
|
||||
defer ss.FileInfo().PermanentDelete(rctx, file2.Id)
|
||||
|
||||
err = ss.FileInfo().AttachToPost(rctx, file2.Id, model.NewId(), channelId, userId)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("save bookmarks", func(t *testing.T) {
|
||||
bookmarkResp, err := ss.ChannelBookmark().Save(bookmark1.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotEmpty(t, bookmarkResp.Id)
|
||||
assert.Equal(t, bookmark1.ChannelId, bookmarkResp.ChannelId)
|
||||
assert.Nil(t, bookmarkResp.FileInfo)
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Save(bookmark2.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotEmpty(t, bookmarkResp.Id)
|
||||
assert.Equal(t, bookmark2.ChannelId, bookmarkResp.ChannelId)
|
||||
assert.NotNil(t, bookmarkResp.FileInfo)
|
||||
|
||||
bookmarks, err := ss.ChannelBookmark().GetBookmarksForChannelSince(channelId, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, bookmarks, 2)
|
||||
|
||||
_, err = ss.ChannelBookmark().Save(bookmark3.Clone(), true)
|
||||
assert.Error(t, err) // Error as the file is attached to a bookmark
|
||||
|
||||
_, err = ss.ChannelBookmark().Save(bookmark4.Clone(), true)
|
||||
assert.Error(t, err) // Error as the file is attached to a post
|
||||
})
|
||||
}
|
||||
|
||||
func testUpdateChannelBookmark(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
channelId := model.NewId()
|
||||
userId := model.NewId()
|
||||
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
}
|
||||
|
||||
t.Run("update bookmark", func(t *testing.T) {
|
||||
bookmarkResp, err := ss.ChannelBookmark().Save(bookmark1.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
now := model.GetMillis()
|
||||
bookmark2 := bookmarkResp.ChannelBookmark.Clone()
|
||||
bookmark2.DisplayName = "Updated display name"
|
||||
bookmark2.Emoji = ":smile:"
|
||||
bookmark2.LinkUrl = "https://mattermost.com/about"
|
||||
|
||||
time.Sleep(time.Millisecond * 250)
|
||||
|
||||
err = ss.ChannelBookmark().Update(bookmark2.Clone())
|
||||
assert.NoError(t, err)
|
||||
|
||||
bookmarks, err := ss.ChannelBookmark().GetBookmarksForChannelSince(channelId, now)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, bookmarks, 1)
|
||||
|
||||
b := find_bookmark(bookmarks, bookmark2.Id)
|
||||
assert.NotNil(t, b)
|
||||
assert.Equal(t, b.DisplayName, bookmark2.DisplayName)
|
||||
assert.Equal(t, b.Type, model.ChannelBookmarkLink)
|
||||
assert.NotEmpty(t, b.Emoji)
|
||||
assert.Equal(t, b.CreateAt, bookmark2.CreateAt)
|
||||
assert.Greater(t, b.UpdateAt, bookmark2.UpdateAt)
|
||||
|
||||
err = ss.ChannelBookmark().Update(bookmark1.Clone())
|
||||
assert.Error(t, err)
|
||||
|
||||
bookmark3 := bookmark2.Clone()
|
||||
bookmark3.Type = model.ChannelBookmarkFile
|
||||
err = ss.ChannelBookmark().Update(bookmark3)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func testUpdateSortOrderChannelBookmark(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
channelId := model.NewId()
|
||||
userId := model.NewId()
|
||||
|
||||
bookmark0 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Bookmark 0",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
file := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Bookmark 1",
|
||||
FileId: file.Id,
|
||||
Type: model.ChannelBookmarkFile,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
_, err := ss.FileInfo().Save(rctx, file)
|
||||
require.NoError(t, err)
|
||||
defer ss.FileInfo().PermanentDelete(rctx, file.Id)
|
||||
|
||||
bookmark2 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Bookmark 2",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
}
|
||||
|
||||
bookmark3 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Bookmark 3",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
}
|
||||
|
||||
bookmark4 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Bookmark 4",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
}
|
||||
|
||||
bookmarkResp, err := ss.ChannelBookmark().Save(bookmark0.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark0 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
assert.NotEmpty(t, bookmarkResp.Id)
|
||||
assert.Equal(t, bookmark0.ChannelId, bookmarkResp.ChannelId)
|
||||
assert.Nil(t, bookmarkResp.FileInfo)
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Save(bookmark1.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark1 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Save(bookmark2.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark2 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Save(bookmark3.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark3 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Save(bookmark4.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark4 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
t.Run("change order of bookmarks first to last", func(t *testing.T) {
|
||||
bookmarks, sortError := ss.ChannelBookmark().UpdateSortOrder(bookmark0.Id, channelId, 4)
|
||||
assert.NoError(t, sortError)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks last to first", func(t *testing.T) {
|
||||
bookmarks, sortError := ss.ChannelBookmark().UpdateSortOrder(bookmark0.Id, channelId, 0)
|
||||
assert.NoError(t, sortError)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks first to third", func(t *testing.T) {
|
||||
bookmarks, sortError := ss.ChannelBookmark().UpdateSortOrder(bookmark0.Id, channelId, 2)
|
||||
assert.NoError(t, sortError)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
|
||||
// now reset order
|
||||
ss.ChannelBookmark().UpdateSortOrder(bookmark0.Id, channelId, 0)
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks second to third", func(t *testing.T) {
|
||||
bookmarks, sortError := ss.ChannelBookmark().UpdateSortOrder(bookmark1.Id, channelId, 2)
|
||||
assert.NoError(t, sortError)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks third to second", func(t *testing.T) {
|
||||
bookmarks, sortError := ss.ChannelBookmark().UpdateSortOrder(bookmark1.Id, channelId, 1)
|
||||
assert.NoError(t, sortError)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks last to previous last", func(t *testing.T) {
|
||||
bookmarks, sortError := ss.ChannelBookmark().UpdateSortOrder(bookmark4.Id, channelId, 3)
|
||||
assert.NoError(t, sortError)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks last to second", func(t *testing.T) {
|
||||
bookmarks, sortError := ss.ChannelBookmark().UpdateSortOrder(bookmark3.Id, channelId, 1)
|
||||
assert.NoError(t, sortError)
|
||||
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark0.Id).SortOrder, int64(0))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark3.Id).SortOrder, int64(1))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark1.Id).SortOrder, int64(2))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark2.Id).SortOrder, int64(3))
|
||||
assert.Equal(t, find_bookmark(bookmarks, bookmark4.Id).SortOrder, int64(4))
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks error when new index is out of bounds", func(t *testing.T) {
|
||||
var iiErr *store.ErrInvalidInput
|
||||
_, err = ss.ChannelBookmark().UpdateSortOrder(bookmark3.Id, channelId, -1)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorAs(t, err, &iiErr)
|
||||
_, err = ss.ChannelBookmark().UpdateSortOrder(bookmark3.Id, channelId, 5)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorAs(t, err, &iiErr)
|
||||
})
|
||||
|
||||
t.Run("change order of bookmarks error when bookmark not found", func(t *testing.T) {
|
||||
_, err = ss.ChannelBookmark().UpdateSortOrder(model.NewId(), channelId, 0)
|
||||
assert.Error(t, err)
|
||||
var nfErr *store.ErrNotFound
|
||||
assert.ErrorAs(t, err, &nfErr)
|
||||
})
|
||||
}
|
||||
|
||||
func testDeleteChannelBookmark(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
channelId := model.NewId()
|
||||
userId := model.NewId()
|
||||
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
file := &model.FileInfo{
|
||||
Id: model.NewId(),
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "somepath",
|
||||
ThumbnailPath: "thumbpath",
|
||||
PreviewPath: "prevPath",
|
||||
Name: "test file",
|
||||
Extension: "png",
|
||||
MimeType: "images/png",
|
||||
Size: 873182,
|
||||
Width: 3076,
|
||||
Height: 2200,
|
||||
HasPreviewImage: true,
|
||||
}
|
||||
|
||||
bookmark2 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "file bookmark test",
|
||||
FileId: file.Id,
|
||||
Type: model.ChannelBookmarkFile,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
_, err := ss.FileInfo().Save(rctx, file)
|
||||
require.NoError(t, err)
|
||||
defer ss.FileInfo().PermanentDelete(rctx, file.Id)
|
||||
|
||||
t.Run("delete bookmark", func(t *testing.T) {
|
||||
now := model.GetMillis()
|
||||
bookmarkResp, err := ss.ChannelBookmark().Save(bookmark1.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark1 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
assert.NotEmpty(t, bookmarkResp.Id)
|
||||
assert.Equal(t, bookmark1.ChannelId, bookmarkResp.ChannelId)
|
||||
assert.Nil(t, bookmarkResp.FileInfo)
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Save(bookmark2.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark2 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
err = ss.ChannelBookmark().Delete(bookmark2.Id, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
bookmarks, err := ss.ChannelBookmark().GetBookmarksForChannelSince(channelId, now)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, bookmarks, 2) // we have two as the deleted record also gets returned for sync'ing purposes
|
||||
|
||||
b := find_bookmark(bookmarks, bookmark2.Id)
|
||||
assert.NotNil(t, b)
|
||||
assert.Equal(t, bookmarks[0].Type, model.ChannelBookmarkLink)
|
||||
})
|
||||
}
|
||||
|
||||
func testGetChannelBookmark(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
channelId := model.NewId()
|
||||
userId := model.NewId()
|
||||
|
||||
bookmark1 := &model.ChannelBookmark{
|
||||
ChannelId: channelId,
|
||||
OwnerId: userId,
|
||||
DisplayName: "Link bookmark test",
|
||||
LinkUrl: "https://mattermost.com",
|
||||
Type: model.ChannelBookmarkLink,
|
||||
Emoji: ":smile:",
|
||||
}
|
||||
|
||||
t.Run("get bookmark", func(t *testing.T) {
|
||||
bookmarkResp, err := ss.ChannelBookmark().Save(bookmark1.Clone(), true)
|
||||
assert.NoError(t, err)
|
||||
bookmark1 = bookmarkResp.ChannelBookmark.Clone()
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Get(bookmark1.Id, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NotEmpty(t, bookmarkResp.Id)
|
||||
assert.Equal(t, bookmark1.ChannelId, bookmarkResp.ChannelId)
|
||||
assert.Nil(t, bookmarkResp.FileInfo)
|
||||
|
||||
err = ss.ChannelBookmark().Delete(bookmark1.Id, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Get(bookmark1.Id, false)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, bookmarkResp)
|
||||
|
||||
bookmarkResp, err = ss.ChannelBookmark().Get(bookmark1.Id, true)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, bookmarkResp)
|
||||
})
|
||||
}
|
||||
@@ -615,16 +615,26 @@ func testFileInfoPermanentDeleteBatch(t *testing.T, rctx request.CTX, ss store.S
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
bookmarkFile, err := ss.FileInfo().Save(rctx, &model.FileInfo{ // should not be deleted
|
||||
PostId: postId,
|
||||
ChannelId: channelId,
|
||||
CreatorId: model.BookmarkFileOwner,
|
||||
Path: "file.txt",
|
||||
CreateAt: 1000,
|
||||
})
|
||||
defer ss.FileInfo().PermanentDelete(rctx, bookmarkFile.Id)
|
||||
require.NoError(t, err)
|
||||
|
||||
postFiles, err := ss.FileInfo().GetForPost(postId, true, false, false)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, postFiles, 3)
|
||||
assert.Len(t, postFiles, 4)
|
||||
|
||||
_, err = ss.FileInfo().PermanentDeleteBatch(rctx, 1500, 1000)
|
||||
require.NoError(t, err)
|
||||
|
||||
postFiles, err = ss.FileInfo().GetForPost(postId, true, false, false)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, postFiles, 1)
|
||||
assert.Len(t, postFiles, 2)
|
||||
}
|
||||
|
||||
func testFileInfoPermanentDeleteByUser(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
|
||||
176
server/channels/store/storetest/mocks/ChannelBookmarkStore.go
Обычный файл
176
server/channels/store/storetest/mocks/ChannelBookmarkStore.go
Обычный файл
@@ -0,0 +1,176 @@
|
||||
// Code generated by mockery v2.23.2. DO NOT EDIT.
|
||||
|
||||
// Regenerate this file using `make store-mocks`.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
model "github.com/mattermost/mattermost/server/public/model"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// ChannelBookmarkStore is an autogenerated mock type for the ChannelBookmarkStore type
|
||||
type ChannelBookmarkStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: bookmarkId, deleteFile
|
||||
func (_m *ChannelBookmarkStore) Delete(bookmarkId string, deleteFile bool) error {
|
||||
ret := _m.Called(bookmarkId, deleteFile)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, bool) error); ok {
|
||||
r0 = rf(bookmarkId, deleteFile)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// ErrorIfBookmarkFileInfoAlreadyAttached provides a mock function with given fields: fileId
|
||||
func (_m *ChannelBookmarkStore) ErrorIfBookmarkFileInfoAlreadyAttached(fileId string) error {
|
||||
ret := _m.Called(fileId)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string) error); ok {
|
||||
r0 = rf(fileId)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: Id, includeDeleted
|
||||
func (_m *ChannelBookmarkStore) Get(Id string, includeDeleted bool) (*model.ChannelBookmarkWithFileInfo, error) {
|
||||
ret := _m.Called(Id, includeDeleted)
|
||||
|
||||
var r0 *model.ChannelBookmarkWithFileInfo
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, bool) (*model.ChannelBookmarkWithFileInfo, error)); ok {
|
||||
return rf(Id, includeDeleted)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, bool) *model.ChannelBookmarkWithFileInfo); ok {
|
||||
r0 = rf(Id, includeDeleted)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.ChannelBookmarkWithFileInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, bool) error); ok {
|
||||
r1 = rf(Id, includeDeleted)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetBookmarksForChannelSince provides a mock function with given fields: channelId, since
|
||||
func (_m *ChannelBookmarkStore) GetBookmarksForChannelSince(channelId string, since int64) ([]*model.ChannelBookmarkWithFileInfo, error) {
|
||||
ret := _m.Called(channelId, since)
|
||||
|
||||
var r0 []*model.ChannelBookmarkWithFileInfo
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, int64) ([]*model.ChannelBookmarkWithFileInfo, error)); ok {
|
||||
return rf(channelId, since)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, int64) []*model.ChannelBookmarkWithFileInfo); ok {
|
||||
r0 = rf(channelId, since)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.ChannelBookmarkWithFileInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, int64) error); ok {
|
||||
r1 = rf(channelId, since)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Save provides a mock function with given fields: bookmark, increaseSortOrder
|
||||
func (_m *ChannelBookmarkStore) Save(bookmark *model.ChannelBookmark, increaseSortOrder bool) (*model.ChannelBookmarkWithFileInfo, error) {
|
||||
ret := _m.Called(bookmark, increaseSortOrder)
|
||||
|
||||
var r0 *model.ChannelBookmarkWithFileInfo
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(*model.ChannelBookmark, bool) (*model.ChannelBookmarkWithFileInfo, error)); ok {
|
||||
return rf(bookmark, increaseSortOrder)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(*model.ChannelBookmark, bool) *model.ChannelBookmarkWithFileInfo); ok {
|
||||
r0 = rf(bookmark, increaseSortOrder)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.ChannelBookmarkWithFileInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(*model.ChannelBookmark, bool) error); ok {
|
||||
r1 = rf(bookmark, increaseSortOrder)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Update provides a mock function with given fields: bookmark
|
||||
func (_m *ChannelBookmarkStore) Update(bookmark *model.ChannelBookmark) error {
|
||||
ret := _m.Called(bookmark)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(*model.ChannelBookmark) error); ok {
|
||||
r0 = rf(bookmark)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateSortOrder provides a mock function with given fields: bookmarkId, channelId, newIndex
|
||||
func (_m *ChannelBookmarkStore) UpdateSortOrder(bookmarkId string, channelId string, newIndex int64) ([]*model.ChannelBookmarkWithFileInfo, error) {
|
||||
ret := _m.Called(bookmarkId, channelId, newIndex)
|
||||
|
||||
var r0 []*model.ChannelBookmarkWithFileInfo
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64) ([]*model.ChannelBookmarkWithFileInfo, error)); ok {
|
||||
return rf(bookmarkId, channelId, newIndex)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, string, int64) []*model.ChannelBookmarkWithFileInfo); ok {
|
||||
r0 = rf(bookmarkId, channelId, newIndex)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.ChannelBookmarkWithFileInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, string, int64) error); ok {
|
||||
r1 = rf(bookmarkId, channelId, newIndex)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewChannelBookmarkStore interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewChannelBookmarkStore creates a new instance of ChannelBookmarkStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewChannelBookmarkStore(t mockConstructorTestingTNewChannelBookmarkStore) *ChannelBookmarkStore {
|
||||
mock := &ChannelBookmarkStore{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -72,6 +72,22 @@ func (_m *Store) Channel() store.ChannelStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// ChannelBookmark provides a mock function with given fields:
|
||||
func (_m *Store) ChannelBookmark() store.ChannelBookmarkStore {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.ChannelBookmarkStore
|
||||
if rf, ok := ret.Get(0).(func() store.ChannelBookmarkStore); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.ChannelBookmarkStore)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// ChannelMemberHistory provides a mock function with given fields:
|
||||
func (_m *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -65,6 +65,7 @@ type Store struct {
|
||||
PostPersistentNotificationStore mocks.PostPersistentNotificationStore
|
||||
TrueUpReviewStore mocks.TrueUpReviewStore
|
||||
DesktopTokensStore mocks.DesktopTokensStore
|
||||
ChannelBookmarkStore mocks.ChannelBookmarkStore
|
||||
}
|
||||
|
||||
func (s *Store) SetContext(context context.Context) { s.context = context }
|
||||
@@ -110,13 +111,14 @@ func (s *Store) Draft() store.DraftStore { return &s.D
|
||||
func (s *Store) ChannelMemberHistory() store.ChannelMemberHistoryStore {
|
||||
return &s.ChannelMemberHistoryStore
|
||||
}
|
||||
func (s *Store) TrueUpReview() store.TrueUpReviewStore { return &s.TrueUpReviewStore }
|
||||
func (s *Store) DesktopTokens() store.DesktopTokensStore { return &s.DesktopTokensStore }
|
||||
func (s *Store) NotifyAdmin() store.NotifyAdminStore { return &s.NotifyAdminStore }
|
||||
func (s *Store) Group() store.GroupStore { return &s.GroupStore }
|
||||
func (s *Store) LinkMetadata() store.LinkMetadataStore { return &s.LinkMetadataStore }
|
||||
func (s *Store) SharedChannel() store.SharedChannelStore { return &s.SharedChannelStore }
|
||||
func (s *Store) PostPriority() store.PostPriorityStore { return &s.PostPriorityStore }
|
||||
func (s *Store) ChannelBookmark() store.ChannelBookmarkStore { return &s.ChannelBookmarkStore }
|
||||
func (s *Store) TrueUpReview() store.TrueUpReviewStore { return &s.TrueUpReviewStore }
|
||||
func (s *Store) DesktopTokens() store.DesktopTokensStore { return &s.DesktopTokensStore }
|
||||
func (s *Store) NotifyAdmin() store.NotifyAdminStore { return &s.NotifyAdminStore }
|
||||
func (s *Store) Group() store.GroupStore { return &s.GroupStore }
|
||||
func (s *Store) LinkMetadata() store.LinkMetadataStore { return &s.LinkMetadataStore }
|
||||
func (s *Store) SharedChannel() store.SharedChannelStore { return &s.SharedChannelStore }
|
||||
func (s *Store) PostPriority() store.PostPriorityStore { return &s.PostPriorityStore }
|
||||
func (s *Store) PostAcknowledgement() store.PostAcknowledgementStore {
|
||||
return &s.PostAcknowledgementStore
|
||||
}
|
||||
@@ -187,5 +189,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
|
||||
&s.PostAcknowledgementStore,
|
||||
&s.PostPersistentNotificationStore,
|
||||
&s.DesktopTokensStore,
|
||||
&s.ChannelBookmarkStore,
|
||||
)
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user