diff options
author | Jonathan <jonfritz@gmail.com> | 2017-12-12 09:42:29 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-12 09:42:29 -0500 |
commit | ab30c4daf935d33b7e6088d82eb7c49dc2717918 (patch) | |
tree | 685605246a589cea8bb490f022672b28b338e043 /store/sqlstore | |
parent | fa9aa85705a86db8091aba2b4ff064e127c940ff (diff) | |
download | chat-ab30c4daf935d33b7e6088d82eb7c49dc2717918.tar.gz chat-ab30c4daf935d33b7e6088d82eb7c49dc2717918.tar.bz2 chat-ab30c4daf935d33b7e6088d82eb7c49dc2717918.zip |
PLT-8297: Message Export Should Still Produce Valid Exports if ChannelMemberHistory Data is Missing (#7951)
* Added a less accurate ChannelMembers fallback that is used if export period occurs before MessageExport was enabled
* Fixed test names
* Improved testing
* Made hasDataFromBefore() a little less strict
* Fixed the test to cleanly truncate the ChannelMemberHistory table without exposing the db via the interface
* Renamed a function for better clarity
* Binary logic is hard
Diffstat (limited to 'store/sqlstore')
-rw-r--r-- | store/sqlstore/channel_member_history_store.go | 82 |
1 files changed, 73 insertions, 9 deletions
diff --git a/store/sqlstore/channel_member_history_store.go b/store/sqlstore/channel_member_history_store.go index aa5037d32..182f37ce9 100644 --- a/store/sqlstore/channel_member_history_store.go +++ b/store/sqlstore/channel_member_history_store.go @@ -6,6 +6,8 @@ package sqlstore import ( "net/http" + "database/sql" + l4g "github.com/alecthomas/log4go" "github.com/mattermost/mattermost-server/model" "github.com/mattermost/mattermost-server/store" @@ -65,7 +67,47 @@ func (s SqlChannelMemberHistoryStore) LogLeaveEvent(userId string, channelId str func (s SqlChannelMemberHistoryStore) GetUsersInChannelDuring(startTime int64, endTime int64, channelId string) store.StoreChannel { return store.Do(func(result *store.StoreResult) { - query := ` + if useChannelMemberHistory, err := s.hasDataAtOrBefore(startTime); err != nil { + result.Err = model.NewAppError("SqlChannelMemberHistoryStore.GetUsersInChannelAt", "store.sql_channel_member_history.get_users_in_channel_during.app_error", nil, err.Error(), http.StatusInternalServerError) + } else if useChannelMemberHistory { + // the export period starts after the ChannelMemberHistory table was first introduced, so we can use the + // data from it for our export + if channelMemberHistories, err := s.getFromChannelMemberHistoryTable(startTime, endTime, channelId); err != nil { + result.Err = model.NewAppError("SqlChannelMemberHistoryStore.GetUsersInChannelAt", "store.sql_channel_member_history.get_users_in_channel_during.app_error", nil, err.Error(), http.StatusInternalServerError) + } else { + result.Data = channelMemberHistories + } + } else { + // the export period starts before the ChannelMemberHistory table was introduced, so we need to fake the + // data by assuming that anybody who has ever joined the channel in question was present during the export period. + // this may not always be true, but it's better than saying that somebody wasn't there when they were + if channelMemberHistories, err := s.getFromChannelMembersTable(startTime, endTime, channelId); err != nil { + result.Err = model.NewAppError("SqlChannelMemberHistoryStore.GetUsersInChannelAt", "store.sql_channel_member_history.get_users_in_channel_during.app_error", nil, err.Error(), http.StatusInternalServerError) + } else { + result.Data = channelMemberHistories + } + } + }) +} + +func (s SqlChannelMemberHistoryStore) hasDataAtOrBefore(time int64) (bool, error) { + type NullableCountResult struct { + Min sql.NullInt64 + } + var result NullableCountResult + query := "SELECT MIN(JoinTime) AS Min FROM ChannelMemberHistory" + if err := s.GetReplica().SelectOne(&result, query); err != nil { + return false, err + } else if result.Min.Valid { + return result.Min.Int64 <= time, nil + } else { + // if the result was null, there are no rows in the table, so there is no data from before + return false, nil + } +} + +func (s SqlChannelMemberHistoryStore) getFromChannelMemberHistoryTable(startTime int64, endTime int64, channelId string) ([]*model.ChannelMemberHistory, error) { + query := ` SELECT cmh.*, u.Email @@ -76,14 +118,37 @@ func (s SqlChannelMemberHistoryStore) GetUsersInChannelDuring(startTime int64, e AND (cmh.LeaveTime IS NULL OR cmh.LeaveTime >= :StartTime) ORDER BY cmh.JoinTime ASC` - params := map[string]interface{}{"ChannelId": channelId, "StartTime": startTime, "EndTime": endTime} - var histories []*model.ChannelMemberHistory - if _, err := s.GetReplica().Select(&histories, query, params); err != nil { - result.Err = model.NewAppError("SqlChannelMemberHistoryStore.GetUsersInChannelAt", "store.sql_channel_member_history.get_users_in_channel_during.app_error", params, err.Error(), http.StatusInternalServerError) - } else { - result.Data = histories + params := map[string]interface{}{"ChannelId": channelId, "StartTime": startTime, "EndTime": endTime} + var histories []*model.ChannelMemberHistory + if _, err := s.GetReplica().Select(&histories, query, params); err != nil { + return nil, err + } else { + return histories, nil + } +} + +func (s SqlChannelMemberHistoryStore) getFromChannelMembersTable(startTime int64, endTime int64, channelId string) ([]*model.ChannelMemberHistory, error) { + query := ` + SELECT DISTINCT + ch.ChannelId, + ch.UserId, + u.email + FROM ChannelMembers AS ch + INNER JOIN Users AS u ON ch.UserId = u.id + WHERE ch.ChannelId = :ChannelId` + + params := map[string]interface{}{"ChannelId": channelId} + var histories []*model.ChannelMemberHistory + if _, err := s.GetReplica().Select(&histories, query, params); err != nil { + return nil, err + } else { + // we have to fill in the join/leave times, because that data doesn't exist in the channel members table + for _, channelMemberHistory := range histories { + channelMemberHistory.JoinTime = startTime + channelMemberHistory.LeaveTime = model.NewInt64(endTime) } - }) + return histories, nil + } } func (s SqlChannelMemberHistoryStore) PermanentDeleteBatch(endTime int64, limit int64) store.StoreChannel { @@ -112,7 +177,6 @@ func (s SqlChannelMemberHistoryStore) PermanentDeleteBatch(endTime int64, limit } else { if rowsAffected, err1 := sqlResult.RowsAffected(); err1 != nil { result.Err = model.NewAppError("SqlChannelMemberHistoryStore.PermanentDeleteBatchForChannel", "store.sql_channel_member_history.permanent_delete_batch.app_error", params, err.Error(), http.StatusInternalServerError) - result.Data = int64(0) } else { result.Data = rowsAffected } |