https://mattermost.atlassian.net/browse/MM-52532 - Replace golint with revive - Add makezero linter - Fix all the required linter failures Some issues in enterprise and public modules are yet to be fixed. We send this to expediate things.
266 строки
9.0 KiB
Go
266 строки
9.0 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"net/http"
|
|
"sort"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
)
|
|
|
|
type filterPostOptions struct {
|
|
assumeSortedCreatedAt bool
|
|
}
|
|
|
|
type accessibleBounds struct {
|
|
start int
|
|
end int
|
|
}
|
|
|
|
func (b accessibleBounds) allAccessible(lenPosts int) bool {
|
|
return b.start == allAccessibleBounds(lenPosts).start && b.end == allAccessibleBounds(lenPosts).end
|
|
}
|
|
|
|
func (b accessibleBounds) noAccessible() bool {
|
|
return b.start == noAccessibleBounds.start && b.end == noAccessibleBounds.end
|
|
}
|
|
|
|
// assumes checking was already performed that at least one post is inaccessible
|
|
func (b accessibleBounds) getInaccessibleRange(listLength int) (int, int) {
|
|
var start, end int
|
|
if b.start == 0 {
|
|
start = b.end + 1
|
|
end = listLength - 1
|
|
} else {
|
|
start = 0
|
|
end = b.start - 1
|
|
}
|
|
return start, end
|
|
}
|
|
|
|
var noAccessibleBounds = accessibleBounds{start: -1, end: -1}
|
|
var allAccessibleBounds = func(lenPosts int) accessibleBounds { return accessibleBounds{start: 0, end: lenPosts - 1} }
|
|
|
|
// getTimeSortedPostAccessibleBounds returns what the boundaries are for accessible posts.
|
|
// It assumes that CreateAt time for posts is monotonically increasing or decreasing.
|
|
// It could be either because posts can be returned in ascending or descending time order.
|
|
// Special values (which can be checked with methods `allAccessible` and `allInaccessible`)
|
|
// denote if all or none of the posts are accessible.
|
|
func getTimeSortedPostAccessibleBounds(earliestAccessibleTime int64, lenPosts int, getCreateAt func(int) int64) accessibleBounds {
|
|
if lenPosts == 0 {
|
|
return allAccessibleBounds(lenPosts)
|
|
}
|
|
if lenPosts == 1 {
|
|
if getCreateAt(0) >= earliestAccessibleTime {
|
|
return allAccessibleBounds(lenPosts)
|
|
}
|
|
return noAccessibleBounds
|
|
}
|
|
|
|
ascending := getCreateAt(0) < getCreateAt(lenPosts-1)
|
|
|
|
idx := sort.Search(lenPosts, func(i int) bool {
|
|
if ascending {
|
|
// Ascending order automatically picks the left most post(at idx),
|
|
// in case multiple posts at idx, idx+1, idx+2... have the same time.
|
|
return getCreateAt(i) >= earliestAccessibleTime
|
|
}
|
|
// Special case(subtracting 1) for descending order to include the right most post(at idx+k),
|
|
// in case multiple posts at idx, idx+1, idx+2...idx+k have the same time.
|
|
return getCreateAt(i) <= earliestAccessibleTime-1
|
|
})
|
|
|
|
if ascending {
|
|
if idx == lenPosts {
|
|
return noAccessibleBounds
|
|
}
|
|
return accessibleBounds{start: idx, end: lenPosts - 1}
|
|
}
|
|
|
|
if idx == 0 {
|
|
return noAccessibleBounds
|
|
}
|
|
return accessibleBounds{start: 0, end: idx - 1}
|
|
}
|
|
|
|
// linearFilterPostList make no assumptions about ordering, go through posts one by one
|
|
// this is the slower fallback that is still safe if we can not
|
|
// assume posts are ordered by CreatedAt
|
|
func linearFilterPostList(postList *model.PostList, earliestAccessibleTime int64) {
|
|
// filter Posts
|
|
posts := postList.Posts
|
|
order := postList.Order
|
|
|
|
n := 0
|
|
for i, postId := range order {
|
|
if createAt := posts[postId].CreateAt; createAt >= earliestAccessibleTime {
|
|
order[n] = order[i]
|
|
n++
|
|
} else {
|
|
if createAt > postList.FirstInaccessiblePostTime {
|
|
postList.FirstInaccessiblePostTime = createAt
|
|
}
|
|
delete(posts, postId)
|
|
}
|
|
}
|
|
postList.Order = order[:n]
|
|
|
|
// it can happen that some post list results don't have all posts in the Order field.
|
|
// for example GetPosts in the CollapsedThreads = false path, parents are not added
|
|
// to Order
|
|
for postId := range posts {
|
|
if createAt := posts[postId].CreateAt; createAt < earliestAccessibleTime {
|
|
if createAt > postList.FirstInaccessiblePostTime {
|
|
postList.FirstInaccessiblePostTime = createAt
|
|
}
|
|
delete(posts, postId)
|
|
}
|
|
}
|
|
}
|
|
|
|
// linearFilterPostsSlice make no assumptions about ordering, go through posts one by one
|
|
// this is the slower fallback that is still safe if we can not
|
|
// assume posts are ordered by CreatedAt
|
|
func linearFilterPostsSlice(posts []*model.Post, earliestAccessibleTime int64) ([]*model.Post, int64) {
|
|
var firstInaccessiblePostTime int64
|
|
n := 0
|
|
for i := range posts {
|
|
if createAt := posts[i].CreateAt; createAt >= earliestAccessibleTime {
|
|
posts[n] = posts[i]
|
|
n++
|
|
} else {
|
|
if createAt > firstInaccessiblePostTime {
|
|
firstInaccessiblePostTime = createAt
|
|
}
|
|
}
|
|
}
|
|
return posts[:n], firstInaccessiblePostTime
|
|
}
|
|
|
|
// filterInaccessiblePosts filters out the posts, past the cloud limit
|
|
func (a *App) filterInaccessiblePosts(postList *model.PostList, options filterPostOptions) *model.AppError {
|
|
if postList == nil || postList.Posts == nil || len(postList.Posts) == 0 {
|
|
return nil
|
|
}
|
|
|
|
lastAccessiblePostTime, appErr := a.GetLastAccessiblePostTime()
|
|
if appErr != nil {
|
|
return model.NewAppError("filterInaccessiblePosts", "app.last_accessible_post.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
|
}
|
|
if lastAccessiblePostTime == 0 {
|
|
// No need to filter, all posts are accessible
|
|
return nil
|
|
}
|
|
|
|
if len(postList.Posts) == len(postList.Order) && options.assumeSortedCreatedAt {
|
|
lenPosts := len(postList.Posts)
|
|
getCreateAt := func(i int) int64 { return postList.Posts[postList.Order[i]].CreateAt }
|
|
|
|
bounds := getTimeSortedPostAccessibleBounds(lastAccessiblePostTime, lenPosts, getCreateAt)
|
|
|
|
if bounds.allAccessible(lenPosts) {
|
|
return nil
|
|
}
|
|
if bounds.noAccessible() {
|
|
if lenPosts > 0 {
|
|
firstPostCreatedAt := postList.Posts[postList.Order[0]].CreateAt
|
|
lastPostCreatedAt := postList.Posts[postList.Order[len(postList.Order)-1]].CreateAt
|
|
postList.FirstInaccessiblePostTime = max(firstPostCreatedAt, lastPostCreatedAt)
|
|
}
|
|
postList.Posts = map[string]*model.Post{}
|
|
postList.Order = []string{}
|
|
return nil
|
|
}
|
|
startInaccessibleIndex, endInaccessibleIndex := bounds.getInaccessibleRange(len(postList.Order))
|
|
startInaccessibleCreatedAt := postList.Posts[postList.Order[startInaccessibleIndex]].CreateAt
|
|
endInaccessibleCreatedAt := postList.Posts[postList.Order[endInaccessibleIndex]].CreateAt
|
|
postList.FirstInaccessiblePostTime = max(startInaccessibleCreatedAt, endInaccessibleCreatedAt)
|
|
|
|
posts := postList.Posts
|
|
order := postList.Order
|
|
accessibleCount := bounds.end - bounds.start + 1
|
|
inaccessibleCount := lenPosts - accessibleCount
|
|
// Linearly cover shorter route to traverse posts map
|
|
if inaccessibleCount < accessibleCount {
|
|
for i := 0; i < bounds.start; i++ {
|
|
delete(posts, order[i])
|
|
}
|
|
for i := bounds.end + 1; i < lenPosts; i++ {
|
|
delete(posts, order[i])
|
|
}
|
|
} else {
|
|
accessiblePosts := make(map[string]*model.Post, accessibleCount)
|
|
for i := bounds.start; i <= bounds.end; i++ {
|
|
accessiblePosts[order[i]] = posts[order[i]]
|
|
}
|
|
postList.Posts = accessiblePosts
|
|
}
|
|
|
|
postList.Order = postList.Order[bounds.start : bounds.end+1]
|
|
} else {
|
|
linearFilterPostList(postList, lastAccessiblePostTime)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// isInaccessiblePost indicates if the post is past the cloud plan's limit.
|
|
func (a *App) isInaccessiblePost(post *model.Post) (int64, *model.AppError) {
|
|
if post == nil {
|
|
return 0, nil
|
|
}
|
|
|
|
pl := &model.PostList{
|
|
Order: []string{post.Id},
|
|
Posts: map[string]*model.Post{post.Id: post},
|
|
}
|
|
|
|
return pl.FirstInaccessiblePostTime, a.filterInaccessiblePosts(pl, filterPostOptions{assumeSortedCreatedAt: true})
|
|
}
|
|
|
|
// getFilteredAccessiblePosts returns accessible posts filtered as per the cloud plan's limit and also indicates if there were any inaccessible posts
|
|
func (a *App) getFilteredAccessiblePosts(posts []*model.Post, options filterPostOptions) ([]*model.Post, int64, *model.AppError) {
|
|
if len(posts) == 0 {
|
|
return posts, 0, nil
|
|
}
|
|
|
|
filteredPosts := []*model.Post{}
|
|
lastAccessiblePostTime, appErr := a.GetLastAccessiblePostTime()
|
|
if appErr != nil {
|
|
return filteredPosts, 0, model.NewAppError("getFilteredAccessiblePosts", "app.last_accessible_post.app_error", nil, "", http.StatusInternalServerError).Wrap(appErr)
|
|
} else if lastAccessiblePostTime == 0 {
|
|
// No need to filter, all posts are accessible
|
|
return posts, 0, nil
|
|
}
|
|
|
|
if options.assumeSortedCreatedAt {
|
|
lenPosts := len(posts)
|
|
getCreateAt := func(i int) int64 { return posts[i].CreateAt }
|
|
bounds := getTimeSortedPostAccessibleBounds(lastAccessiblePostTime, lenPosts, getCreateAt)
|
|
if bounds.allAccessible(lenPosts) {
|
|
return posts, 0, nil
|
|
}
|
|
if bounds.noAccessible() {
|
|
var firstInaccessiblePostTime int64
|
|
if lenPosts > 0 {
|
|
firstPostCreatedAt := posts[0].CreateAt
|
|
lastPostCreatedAt := posts[len(posts)-1].CreateAt
|
|
firstInaccessiblePostTime = max(firstPostCreatedAt, lastPostCreatedAt)
|
|
}
|
|
return filteredPosts, firstInaccessiblePostTime, nil
|
|
}
|
|
|
|
startInaccessibleIndex, endInaccessibleIndex := bounds.getInaccessibleRange(len(posts))
|
|
firstPostCreatedAt := posts[startInaccessibleIndex].CreateAt
|
|
lastPostCreatedAt := posts[endInaccessibleIndex].CreateAt
|
|
firstInaccessiblePostTime := max(firstPostCreatedAt, lastPostCreatedAt)
|
|
filteredPosts = posts[bounds.start : bounds.end+1]
|
|
return filteredPosts, firstInaccessiblePostTime, nil
|
|
}
|
|
|
|
filteredPosts, firstInaccessiblePostTime := linearFilterPostsSlice(posts, lastAccessiblePostTime)
|
|
return filteredPosts, firstInaccessiblePostTime, nil
|
|
}
|