MM-43956: Adds post counts by duration. (#20131)

* MM-43956: Adds post counts by day.

* MM-43956: Test data cleanup. Switch from Unix to UnixMilli.

* MM-43956: Changes from selecting date data types to strings in SQL.

* MM-43956: Fixes date format key.

* MM-43956: Adds missing user id scope for 'my' top channels graph.

* MM-43956: Adds the ability to group post counts by hour.

* MM-43956: Require enterprise or professional license. Reject guests.

* MM-43956: Adds license for tests.

* MM-43956: Renames function.

* MM-43956: Omits future hours from post counts by hour.

* MM-43956: Adjust API response grouping to users timezone.

* MM-43956: Adds translation.

* MM-43956: Adds user's timezone to the data tier for the grouping by day and hour.

* MM-43956: Fixes layers.

* MM-43956: Lint fix.

* MM-43956: Fix store layers.

* MM-43956: Switches to default name for time package; changes parameter names to avoid naming conflict.

* MM-43956: Updates mocks.

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Martin Kraft
2022-06-13 16:22:34 -04:00
коммит произвёл GitHub
родитель c03eb778c8
Коммит 182ae1234a
29 изменённых файлов: 2055 добавлений и 1533 удалений

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

@@ -148,6 +148,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlStore) {
t.Run("UpdateSidebarChannelsByPreferences", func(t *testing.T) { testUpdateSidebarChannelsByPreferences(t, ss) })
t.Run("SetShared", func(t *testing.T) { testSetShared(t, ss) })
t.Run("GetTeamForChannel", func(t *testing.T) { testGetTeamForChannel(t, ss) })
t.Run("PostCountsByDuration", func(t *testing.T) { testChannelPostCountsByDuration(t, ss) })
}
func testChannelStoreSave(t *testing.T, ss store.Store) {
@@ -7904,3 +7905,38 @@ func testGetTeamForChannel(t *testing.T, ss store.Store) {
var nfErr *store.ErrNotFound
require.True(t, errors.As(err, &nfErr))
}
func testChannelPostCountsByDuration(t *testing.T, ss store.Store) {
team, err := ss.Team().Save(&model.Team{
Name: model.NewId(),
DisplayName: "DisplayName",
Email: MakeEmail(),
Type: model.TeamOpen,
})
require.NoError(t, err)
defer func() { ss.Team().PermanentDelete(team.Id) }()
channel := &model.Channel{
TeamId: team.Id,
DisplayName: "test_share_flag",
Name: "test_share_flag",
Type: model.ChannelTypeOpen,
}
channelSaved, err := ss.Channel().Save(channel, 999)
require.NoError(t, err)
defer func() { ss.Channel().PermanentDelete(channelSaved.Id) }()
userID := model.NewId()
_, err = ss.Post().Save(&model.Post{
UserId: userID,
ChannelId: channel.Id,
Message: "test",
})
require.NoError(t, err)
dpc, err := ss.Channel().PostCountsByDuration([]string{channelSaved.Id}, 0, &userID, model.PostsByDay, time.Now().Location())
require.NoError(t, err)
require.Len(t, dpc, 1)
require.Equal(t, channel.Id, dpc[0].ChannelID)
require.Equal(t, 1, dpc[0].PostCount)
}

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

@@ -11,6 +11,8 @@ import (
mock "github.com/stretchr/testify/mock"
store "github.com/mattermost/mattermost-server/v6/store"
time "time"
)
// ChannelStore is an autogenerated mock type for the ChannelStore type
@@ -266,13 +268,13 @@ func (_m *ChannelStore) CreateSidebarCategory(userID string, teamID string, newC
return r0, r1
}
// Delete provides a mock function with given fields: channelID, time
func (_m *ChannelStore) Delete(channelID string, time int64) error {
ret := _m.Called(channelID, time)
// Delete provides a mock function with given fields: channelID, timestamp
func (_m *ChannelStore) Delete(channelID string, timestamp int64) error {
ret := _m.Called(channelID, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(channelID, time)
r0 = rf(channelID, timestamp)
} else {
r0 = ret.Error(0)
}
@@ -1717,6 +1719,29 @@ func (_m *ChannelStore) PermanentDeleteMembersByUser(userID string) error {
return r0
}
// PostCountsByDuration provides a mock function with given fields: channelIDs, sinceUnixMillis, userID, duration, groupingLocation
func (_m *ChannelStore) PostCountsByDuration(channelIDs []string, sinceUnixMillis int64, userID *string, duration model.PostCountGrouping, groupingLocation *time.Location) ([]*model.DurationPostCount, error) {
ret := _m.Called(channelIDs, sinceUnixMillis, userID, duration, groupingLocation)
var r0 []*model.DurationPostCount
if rf, ok := ret.Get(0).(func([]string, int64, *string, model.PostCountGrouping, *time.Location) []*model.DurationPostCount); ok {
r0 = rf(channelIDs, sinceUnixMillis, userID, duration, groupingLocation)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.DurationPostCount)
}
}
var r1 error
if rf, ok := ret.Get(1).(func([]string, int64, *string, model.PostCountGrouping, *time.Location) error); ok {
r1 = rf(channelIDs, sinceUnixMillis, userID, duration, groupingLocation)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RemoveAllDeactivatedMembers provides a mock function with given fields: channelID
func (_m *ChannelStore) RemoveAllDeactivatedMembers(channelID string) error {
ret := _m.Called(channelID)
@@ -1773,13 +1798,13 @@ func (_m *ChannelStore) ResetAllChannelSchemes() error {
return r0
}
// Restore provides a mock function with given fields: channelID, time
func (_m *ChannelStore) Restore(channelID string, time int64) error {
ret := _m.Called(channelID, time)
// Restore provides a mock function with given fields: channelID, timestamp
func (_m *ChannelStore) Restore(channelID string, timestamp int64) error {
ret := _m.Called(channelID, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(channelID, time)
r0 = rf(channelID, timestamp)
} else {
r0 = ret.Error(0)
}

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

@@ -35,13 +35,13 @@ func (_m *CommandStore) AnalyticsCommandCount(teamID string) (int64, error) {
return r0, r1
}
// Delete provides a mock function with given fields: commandID, time
func (_m *CommandStore) Delete(commandID string, time int64) error {
ret := _m.Called(commandID, time)
// Delete provides a mock function with given fields: commandID, timestamp
func (_m *CommandStore) Delete(commandID string, timestamp int64) error {
ret := _m.Called(commandID, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(commandID, time)
r0 = rf(commandID, timestamp)
} else {
r0 = ret.Error(0)
}

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

@@ -16,13 +16,13 @@ type EmojiStore struct {
mock.Mock
}
// Delete provides a mock function with given fields: emoji, time
func (_m *EmojiStore) Delete(emoji *model.Emoji, time int64) error {
ret := _m.Called(emoji, time)
// Delete provides a mock function with given fields: emoji, timestamp
func (_m *EmojiStore) Delete(emoji *model.Emoji, timestamp int64) error {
ret := _m.Called(emoji, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(*model.Emoji, int64) error); ok {
r0 = rf(emoji, time)
r0 = rf(emoji, timestamp)
} else {
r0 = ret.Error(0)
}

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

@@ -88,13 +88,13 @@ func (_m *PostStore) ClearCaches() {
_m.Called()
}
// Delete provides a mock function with given fields: postID, time, deleteByID
func (_m *PostStore) Delete(postID string, time int64, deleteByID string) error {
ret := _m.Called(postID, time, deleteByID)
// Delete provides a mock function with given fields: postID, timestamp, deleteByID
func (_m *PostStore) Delete(postID string, timestamp int64, deleteByID string) error {
ret := _m.Called(postID, timestamp, deleteByID)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64, string) error); ok {
r0 = rf(postID, time, deleteByID)
r0 = rf(postID, timestamp, deleteByID)
} else {
r0 = ret.Error(0)
}
@@ -354,13 +354,13 @@ func (_m *PostStore) GetParentsForExportAfter(limit int, afterID string) ([]*mod
return r0, r1
}
// GetPostAfterTime provides a mock function with given fields: channelID, time, collapsedThreads
func (_m *PostStore) GetPostAfterTime(channelID string, time int64, collapsedThreads bool) (*model.Post, error) {
ret := _m.Called(channelID, time, collapsedThreads)
// GetPostAfterTime provides a mock function with given fields: channelID, timestamp, collapsedThreads
func (_m *PostStore) GetPostAfterTime(channelID string, timestamp int64, collapsedThreads bool) (*model.Post, error) {
ret := _m.Called(channelID, timestamp, collapsedThreads)
var r0 *model.Post
if rf, ok := ret.Get(0).(func(string, int64, bool) *model.Post); ok {
r0 = rf(channelID, time, collapsedThreads)
r0 = rf(channelID, timestamp, collapsedThreads)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.Post)
@@ -369,7 +369,7 @@ func (_m *PostStore) GetPostAfterTime(channelID string, time int64, collapsedThr
var r1 error
if rf, ok := ret.Get(1).(func(string, int64, bool) error); ok {
r1 = rf(channelID, time, collapsedThreads)
r1 = rf(channelID, timestamp, collapsedThreads)
} else {
r1 = ret.Error(1)
}
@@ -377,20 +377,20 @@ func (_m *PostStore) GetPostAfterTime(channelID string, time int64, collapsedThr
return r0, r1
}
// GetPostIdAfterTime provides a mock function with given fields: channelID, time, collapsedThreads
func (_m *PostStore) GetPostIdAfterTime(channelID string, time int64, collapsedThreads bool) (string, error) {
ret := _m.Called(channelID, time, collapsedThreads)
// GetPostIdAfterTime provides a mock function with given fields: channelID, timestamp, collapsedThreads
func (_m *PostStore) GetPostIdAfterTime(channelID string, timestamp int64, collapsedThreads bool) (string, error) {
ret := _m.Called(channelID, timestamp, collapsedThreads)
var r0 string
if rf, ok := ret.Get(0).(func(string, int64, bool) string); ok {
r0 = rf(channelID, time, collapsedThreads)
r0 = rf(channelID, timestamp, collapsedThreads)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, int64, bool) error); ok {
r1 = rf(channelID, time, collapsedThreads)
r1 = rf(channelID, timestamp, collapsedThreads)
} else {
r1 = ret.Error(1)
}
@@ -398,20 +398,20 @@ func (_m *PostStore) GetPostIdAfterTime(channelID string, time int64, collapsedT
return r0, r1
}
// GetPostIdBeforeTime provides a mock function with given fields: channelID, time, collapsedThreads
func (_m *PostStore) GetPostIdBeforeTime(channelID string, time int64, collapsedThreads bool) (string, error) {
ret := _m.Called(channelID, time, collapsedThreads)
// GetPostIdBeforeTime provides a mock function with given fields: channelID, timestamp, collapsedThreads
func (_m *PostStore) GetPostIdBeforeTime(channelID string, timestamp int64, collapsedThreads bool) (string, error) {
ret := _m.Called(channelID, timestamp, collapsedThreads)
var r0 string
if rf, ok := ret.Get(0).(func(string, int64, bool) string); ok {
r0 = rf(channelID, time, collapsedThreads)
r0 = rf(channelID, timestamp, collapsedThreads)
} else {
r0 = ret.Get(0).(string)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, int64, bool) error); ok {
r1 = rf(channelID, time, collapsedThreads)
r1 = rf(channelID, timestamp, collapsedThreads)
} else {
r1 = ret.Error(1)
}
@@ -534,13 +534,13 @@ func (_m *PostStore) GetPostsByIds(postIds []string) ([]*model.Post, error) {
return r0, r1
}
// GetPostsCreatedAt provides a mock function with given fields: channelID, time
func (_m *PostStore) GetPostsCreatedAt(channelID string, time int64) ([]*model.Post, error) {
ret := _m.Called(channelID, time)
// GetPostsCreatedAt provides a mock function with given fields: channelID, timestamp
func (_m *PostStore) GetPostsCreatedAt(channelID string, timestamp int64) ([]*model.Post, error) {
ret := _m.Called(channelID, timestamp)
var r0 []*model.Post
if rf, ok := ret.Get(0).(func(string, int64) []*model.Post); ok {
r0 = rf(channelID, time)
r0 = rf(channelID, timestamp)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.Post)
@@ -549,7 +549,7 @@ func (_m *PostStore) GetPostsCreatedAt(channelID string, time int64) ([]*model.P
var r1 error
if rf, ok := ret.Get(1).(func(string, int64) error); ok {
r1 = rf(channelID, time)
r1 = rf(channelID, timestamp)
} else {
r1 = ret.Error(1)
}

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

@@ -264,13 +264,13 @@ func (_m *SessionStore) UpdateExpiredNotify(sessionid string, notified bool) err
return r0
}
// UpdateExpiresAt provides a mock function with given fields: sessionID, time
func (_m *SessionStore) UpdateExpiresAt(sessionID string, time int64) error {
ret := _m.Called(sessionID, time)
// UpdateExpiresAt provides a mock function with given fields: sessionID, timestamp
func (_m *SessionStore) UpdateExpiresAt(sessionID string, timestamp int64) error {
ret := _m.Called(sessionID, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(sessionID, time)
r0 = rf(sessionID, timestamp)
} else {
r0 = ret.Error(0)
}
@@ -278,13 +278,13 @@ func (_m *SessionStore) UpdateExpiresAt(sessionID string, time int64) error {
return r0
}
// UpdateLastActivityAt provides a mock function with given fields: sessionID, time
func (_m *SessionStore) UpdateLastActivityAt(sessionID string, time int64) error {
ret := _m.Called(sessionID, time)
// UpdateLastActivityAt provides a mock function with given fields: sessionID, timestamp
func (_m *SessionStore) UpdateLastActivityAt(sessionID string, timestamp int64) error {
ret := _m.Called(sessionID, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(sessionID, time)
r0 = rf(sessionID, timestamp)
} else {
r0 = ret.Error(0)
}

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

@@ -18,20 +18,20 @@ type UserStore struct {
mock.Mock
}
// AnalyticsActiveCount provides a mock function with given fields: time, options
func (_m *UserStore) AnalyticsActiveCount(time int64, options model.UserCountOptions) (int64, error) {
ret := _m.Called(time, options)
// AnalyticsActiveCount provides a mock function with given fields: timestamp, options
func (_m *UserStore) AnalyticsActiveCount(timestamp int64, options model.UserCountOptions) (int64, error) {
ret := _m.Called(timestamp, options)
var r0 int64
if rf, ok := ret.Get(0).(func(int64, model.UserCountOptions) int64); ok {
r0 = rf(time, options)
r0 = rf(timestamp, options)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(int64, model.UserCountOptions) error); ok {
r1 = rf(time, options)
r1 = rf(timestamp, options)
} else {
r1 = ret.Error(1)
}

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

@@ -61,13 +61,13 @@ func (_m *WebhookStore) ClearCaches() {
_m.Called()
}
// DeleteIncoming provides a mock function with given fields: webhookID, time
func (_m *WebhookStore) DeleteIncoming(webhookID string, time int64) error {
ret := _m.Called(webhookID, time)
// DeleteIncoming provides a mock function with given fields: webhookID, timestamp
func (_m *WebhookStore) DeleteIncoming(webhookID string, timestamp int64) error {
ret := _m.Called(webhookID, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(webhookID, time)
r0 = rf(webhookID, timestamp)
} else {
r0 = ret.Error(0)
}
@@ -75,13 +75,13 @@ func (_m *WebhookStore) DeleteIncoming(webhookID string, time int64) error {
return r0
}
// DeleteOutgoing provides a mock function with given fields: webhookID, time
func (_m *WebhookStore) DeleteOutgoing(webhookID string, time int64) error {
ret := _m.Called(webhookID, time)
// DeleteOutgoing provides a mock function with given fields: webhookID, timestamp
func (_m *WebhookStore) DeleteOutgoing(webhookID string, timestamp int64) error {
ret := _m.Called(webhookID, timestamp)
var r0 error
if rf, ok := ret.Get(0).(func(string, int64) error); ok {
r0 = rf(webhookID, time)
r0 = rf(webhookID, timestamp)
} else {
r0 = ret.Error(0)
}

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

@@ -36,7 +36,7 @@ func TestPostStore(t *testing.T, ss store.Store, s SqlStore) {
t.Run("GetPosts", func(t *testing.T) { testPostStoreGetPosts(t, ss) })
t.Run("GetPostBeforeAfter", func(t *testing.T) { testPostStoreGetPostBeforeAfter(t, ss) })
t.Run("UserCountsWithPostsByDay", func(t *testing.T) { testUserCountsWithPostsByDay(t, ss) })
t.Run("PostCountsByDay", func(t *testing.T) { testPostCountsByDay(t, ss) })
t.Run("PostCountsByDuration", func(t *testing.T) { testPostCountsByDay(t, ss) })
t.Run("GetFlaggedPostsForTeam", func(t *testing.T) { testPostStoreGetFlaggedPostsForTeam(t, ss, s) })
t.Run("GetFlaggedPosts", func(t *testing.T) { testPostStoreGetFlaggedPosts(t, ss) })
t.Run("GetFlaggedPostsForChannel", func(t *testing.T) { testPostStoreGetFlaggedPostsForChannel(t, ss) })