[GH-6798] added a new api endpoint to get the bulk reactions for posts (#10049)

* 6798 added a new api to get the bulk reactions for posts

* 6798 added the permsission check before getting the reactions

* GH-6798 added a new app function for the new endpoint

* 6798 added a store method to get reactions for multiple posts

* 6798 connected the app function with the new store function

* 6798 fixed the review comments
Этот коммит содержится в:
Pradeep Murugesan
2019-01-03 14:35:08 +01:00
коммит произвёл Joram Wilander
родитель 7b7b89d5ae
Коммит 99160ff0bc
17 изменённых файлов: 342 добавлений и 0 удалений

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

@@ -236,6 +236,12 @@ func (s *LayeredReactionStore) GetForPost(postId string, allowFromCache bool) St
})
}
func (s *LayeredReactionStore) BulkGetForPosts(postIds []string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.ReactionsBulkGetForPosts(s.TmpContext, postIds)
})
}
func (s *LayeredReactionStore) DeleteAllWithEmojiName(emojiName string) StoreChannel {
return s.RunQuery(func(supplier LayeredStoreSupplier) *LayeredStoreSupplierResult {
return supplier.ReactionDeleteAllWithEmojiName(s.TmpContext, emojiName)

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

@@ -29,6 +29,7 @@ type LayeredStoreSupplier interface {
ReactionGetForPost(ctx context.Context, postId string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
ReactionPermanentDeleteBatch(ctx context.Context, endTime int64, limit int64, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult
// Roles
RoleSave(ctx context.Context, role *model.Role, hints ...LayeredStoreHint) *LayeredStoreSupplierResult

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

@@ -51,3 +51,8 @@ func (s *LocalCacheSupplier) ReactionPermanentDeleteBatch(ctx context.Context, e
// expire from the cache in due course.
return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit)
}
func (s *LocalCacheSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// Ignoring this.
return s.Next().ReactionsBulkGetForPosts(ctx, postIds, hints...)
}

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

@@ -54,3 +54,8 @@ func (s *RedisSupplier) ReactionPermanentDeleteBatch(ctx context.Context, endTim
// Ignoring this. It's probably OK to have the emoji slowly expire from Redis.
return s.Next().ReactionPermanentDeleteBatch(ctx, endTime, limit, hints...)
}
func (s *RedisSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...LayeredStoreHint) *LayeredStoreSupplierResult {
// Ignoring this.
return s.Next().ReactionsBulkGetForPosts(ctx, postIds, hints...)
}

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

@@ -103,6 +103,28 @@ func (s *SqlSupplier) ReactionGetForPost(ctx context.Context, postId string, hin
return result
}
func (s *SqlSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()
keys, params := MapStringsToQueryParams(postIds, "postId")
var reactions []*model.Reaction
if _, err := s.GetReplica().Select(&reactions, `SELECT
*
FROM
Reactions
WHERE
PostId IN `+keys+`
ORDER BY
CreateAt`, params); err != nil {
result.Err = model.NewAppError("SqlReactionStore.GetForPost", "store.sql_reaction.bulk_get_for_post_ids.app_error", nil, "", http.StatusInternalServerError)
} else {
result.Data = reactions
}
return result
}
func (s *SqlSupplier) ReactionDeleteAllWithEmojiName(ctx context.Context, emojiName string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
result := store.NewSupplierResult()

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

@@ -467,6 +467,7 @@ type ReactionStore interface {
GetForPost(postId string, allowFromCache bool) StoreChannel
DeleteAllWithEmojiName(emojiName string) StoreChannel
PermanentDeleteBatch(endTime int64, limit int64) StoreChannel
BulkGetForPosts(postIds []string) StoreChannel
}
type JobStore interface {

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

@@ -421,6 +421,29 @@ func (_m *LayeredStoreDatabaseLayer) ReactionSave(ctx context.Context, reaction
return r0
}
// ReactionsBulkGetForPosts provides a mock function with given fields: ctx, postIds, hints
func (_m *LayeredStoreDatabaseLayer) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, postIds)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, postIds, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// Role provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) Role() store.RoleStore {
ret := _m.Called()

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

@@ -145,6 +145,29 @@ func (_m *LayeredStoreSupplier) ReactionSave(ctx context.Context, reaction *mode
return r0
}
// ReactionsBulkGetForPosts provides a mock function with given fields: ctx, postIds, hints
func (_m *LayeredStoreSupplier) ReactionsBulkGetForPosts(ctx context.Context, postIds []string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))
for _i := range hints {
_va[_i] = hints[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, postIds)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *store.LayeredStoreSupplierResult
if rf, ok := ret.Get(0).(func(context.Context, []string, ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult); ok {
r0 = rf(ctx, postIds, hints...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*store.LayeredStoreSupplierResult)
}
}
return r0
}
// RoleDelete provides a mock function with given fields: ctx, roldId, hints
func (_m *LayeredStoreSupplier) RoleDelete(ctx context.Context, roldId string, hints ...store.LayeredStoreHint) *store.LayeredStoreSupplierResult {
_va := make([]interface{}, len(hints))

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

@@ -13,6 +13,22 @@ type ReactionStore struct {
mock.Mock
}
// BulkGetForPosts provides a mock function with given fields: postIds
func (_m *ReactionStore) BulkGetForPosts(postIds []string) store.StoreChannel {
ret := _m.Called(postIds)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func([]string) store.StoreChannel); ok {
r0 = rf(postIds)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
}
}
return r0
}
// Delete provides a mock function with given fields: reaction
func (_m *ReactionStore) Delete(reaction *model.Reaction) store.StoreChannel {
ret := _m.Called(reaction)

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

@@ -14,6 +14,20 @@ type SqlStore struct {
mock.Mock
}
// AlterColumnDefaultIfExists provides a mock function with given fields: tableName, columnName, mySqlColDefault, postgresColDefault
func (_m *SqlStore) AlterColumnDefaultIfExists(tableName string, columnName string, mySqlColDefault *string, postgresColDefault *string) bool {
ret := _m.Called(tableName, columnName, mySqlColDefault, postgresColDefault)
var r0 bool
if rf, ok := ret.Get(0).(func(string, string, *string, *string) bool); ok {
r0 = rf(tableName, columnName, mySqlColDefault, postgresColDefault)
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// AlterColumnTypeIfExists provides a mock function with given fields: tableName, columnName, mySqlColType, postgresColType
func (_m *SqlStore) AlterColumnTypeIfExists(tableName string, columnName string, mySqlColType string, postgresColType string) bool {
ret := _m.Called(tableName, columnName, mySqlColType, postgresColType)

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

@@ -16,6 +16,7 @@ func TestReactionStore(t *testing.T, ss store.Store) {
t.Run("ReactionGetForPost", func(t *testing.T) { testReactionGetForPost(t, ss) })
t.Run("ReactionDeleteAllWithEmojiName", func(t *testing.T) { testReactionDeleteAllWithEmojiName(t, ss) })
t.Run("PermanentDeleteBatch", func(t *testing.T) { testReactionStorePermanentDeleteBatch(t, ss) })
t.Run("ReactionBulkGetForPosts", func(t *testing.T) { testReactionBulkGetForPosts(t, ss) })
}
func testReactionSave(t *testing.T, ss store.Store) {
@@ -348,3 +349,69 @@ func testReactionStorePermanentDeleteBatch(t *testing.T, ss store.Store) {
t.Fatalf("expected 1 reaction. Got: %v", len(returned))
}
}
func testReactionBulkGetForPosts(t *testing.T, ss store.Store) {
postId := model.NewId()
post2Id := model.NewId()
post3Id := model.NewId()
post4Id := model.NewId()
userId := model.NewId()
reactions := []*model.Reaction{
{
UserId: userId,
PostId: postId,
EmojiName: "smile",
},
{
UserId: model.NewId(),
PostId: post2Id,
EmojiName: "smile",
},
{
UserId: userId,
PostId: post3Id,
EmojiName: "sad",
},
{
UserId: userId,
PostId: postId,
EmojiName: "angry",
},
{
UserId: userId,
PostId: post2Id,
EmojiName: "angry",
},
{
UserId: userId,
PostId: post4Id,
EmojiName: "angry",
},
}
for _, reaction := range reactions {
store.Must(ss.Reaction().Save(reaction))
}
postIds := []string{postId, post2Id, post3Id}
if result := <-ss.Reaction().BulkGetForPosts(postIds); result.Err != nil {
t.Fatal(result.Err)
} else if returned := result.Data.([]*model.Reaction); len(returned) != 5 {
t.Fatal("should've returned 5 reactions")
} else {
post4IdFound := false
for _, reaction := range returned {
if reaction.PostId == post4Id {
post4IdFound = true
break
}
}
if post4IdFound {
t.Fatal("Wrong reaction returned")
}
}
}