MM-11272 Added initial post metadata (#9175)

* MM-11272 Added app.PreparePostForClient

* MM-11272 Added app.PreparePostListForClient

* MM-11272 Added EmojiStore.GetMultipleByName

* MM-11272 Added emojis to PreparePostForClient

* MM-11272 Added unit tests for getting reaction counts

* MM-11272 Added unit tests for TestPreparePostForClient

* MM-11272 Added emojis from reactions to Post.Emojis

* MM-11272 Always update post.UpdateAt when reactions change to bust cache

* Fixed merge conflicts

* Moved post metadata-related code into its own file

* Update store mocks

* Fixed typo

* Add missing license headers

* Updated post metadata tests when custom emojis are disabled

* Fix unreliable unit tests

* Fix inconsistent casing in SQL statements

* Fix blank line

* Invalidate store cache after making changes

* Clear post cache synchronously with reactions
Этот коммит содержится в:
Harrison Healey
2018-08-07 16:24:56 -04:00
родитель 2e945e287d
Коммит 48f16b6401
27 изменённых файлов: 902 добавлений и 87 удалений

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

@@ -5,6 +5,7 @@ package sqlstore
import (
"database/sql"
"fmt"
"net/http"
"github.com/mattermost/mattermost-server/einterfaces"
@@ -128,6 +129,27 @@ func (es SqlEmojiStore) GetByName(name string) store.StoreChannel {
})
}
func (es SqlEmojiStore) GetMultipleByName(names []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
keys, params := MapStringsToQueryParams(names, "Emoji")
var emojis []*model.Emoji
if _, err := es.GetReplica().Select(&emojis,
`SELECT
*
FROM
Emoji
WHERE
Name IN `+keys+`
AND DeleteAt = 0`, params); err != nil {
result.Err = model.NewAppError("SqlEmojiStore.GetByName", "store.sql_emoji.get_by_name.app_error", nil, fmt.Sprintf("names=%v, %v", names, err.Error()), http.StatusInternalServerError)
} else {
result.Data = emojis
}
})
}
func (es SqlEmojiStore) GetList(offset, limit int, sort string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var emoji []*model.Emoji
@@ -151,7 +173,7 @@ func (es SqlEmojiStore) GetList(offset, limit int, sort string) store.StoreChann
func (es SqlEmojiStore) Delete(id string, time int64) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
if sqlResult, err := es.GetMaster().Exec(
`Update
`UPDATE
Emoji
SET
DeleteAt = :DeleteAt,

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

@@ -4,7 +4,6 @@
package sqlstore
import (
"bytes"
"fmt"
"net/http"
"regexp"
@@ -1144,19 +1143,9 @@ func (s *SqlPostStore) GetPostsCreatedAt(channelId string, time int64) store.Sto
func (s *SqlPostStore) GetPostsByIds(postIds []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
keys := bytes.Buffer{}
params := make(map[string]interface{})
for i, postId := range postIds {
if keys.Len() > 0 {
keys.WriteString(",")
}
keys, params := MapStringsToQueryParams(postIds, "Post")
key := "Post" + strconv.Itoa(i)
keys.WriteString(":" + key)
params[key] = postId
}
query := `SELECT * FROM Posts WHERE Id in (` + keys.String() + `) ORDER BY CreateAt DESC`
query := `SELECT * FROM Posts WHERE Id IN ` + keys + ` ORDER BY CreateAt DESC`
var posts []*model.Post
_, err := s.GetReplica().Select(&posts, query, params)

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

@@ -192,22 +192,18 @@ func deleteReactionAndUpdatePost(transaction *gorp.Transaction, reaction *model.
}
const (
// Set HasReactions = true if and only if the post has reactions, update UpdateAt only if HasReactions changes
UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY = `UPDATE
Posts
SET
UpdateAt = (CASE
WHEN HasReactions != (SELECT count(0) > 0 FROM Reactions WHERE PostId = :PostId) THEN :UpdateAt
ELSE UpdateAt
END),
UpdateAt = :UpdateAt,
HasReactions = (SELECT count(0) > 0 FROM Reactions WHERE PostId = :PostId)
WHERE
Id = :PostId`
)
func updatePostForReactionsOnDelete(transaction *gorp.Transaction, postId string) error {
_, err := transaction.Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()})
updateAt := model.GetMillis()
_, err := transaction.Exec(UPDATE_POST_HAS_REACTIONS_ON_DELETE_QUERY, map[string]interface{}{"PostId": postId, "UpdateAt": updateAt})
return err
}
@@ -219,7 +215,7 @@ func updatePostForReactionsOnInsert(transaction *gorp.Transaction, postId string
HasReactions = True,
UpdateAt = :UpdateAt
WHERE
Id = :PostId AND HasReactions = False`,
Id = :PostId`,
map[string]interface{}{"PostId": postId, "UpdateAt": model.GetMillis()})
return err

28
store/sqlstore/utils.go Обычный файл
Просмотреть файл

@@ -0,0 +1,28 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package sqlstore
import (
"bytes"
"fmt"
"strconv"
)
// Converts a list of strings into a list of query parameters and a named parameter map that can
// be used as part of a SQL query.
func MapStringsToQueryParams(list []string, paramPrefix string) (string, map[string]interface{}) {
keys := bytes.Buffer{}
params := make(map[string]interface{})
for i, entry := range list {
if keys.Len() > 0 {
keys.WriteString(",")
}
key := paramPrefix + strconv.Itoa(i)
keys.WriteString(":" + key)
params[key] = entry
}
return fmt.Sprintf("(%v)", keys.String()), params
}

32
store/sqlstore/utils_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,32 @@
package sqlstore
import (
"testing"
)
func TestMapStringsToQueryParams(t *testing.T) {
t.Run("one item", func(t *testing.T) {
input := []string{"apple"}
keys, params := MapStringsToQueryParams(input, "Fruit")
if len(params) != 1 || params["Fruit0"] != "apple" {
t.Fatal("returned incorrect params", params)
} else if keys != "(:Fruit0)" {
t.Fatal("returned incorrect query", keys)
}
})
t.Run("multiple items", func(t *testing.T) {
input := []string{"carrot", "tomato", "potato"}
keys, params := MapStringsToQueryParams(input, "Vegetable")
if len(params) != 3 || params["Vegetable0"] != "carrot" ||
params["Vegetable1"] != "tomato" || params["Vegetable2"] != "potato" {
t.Fatal("returned incorrect params", params)
} else if keys != "(:Vegetable0,:Vegetable1,:Vegetable2)" {
t.Fatal("returned incorrect query", keys)
}
})
}