diff --git a/.golangci.yml b/.golangci.yml index 22a6ae05..90e46c13 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,11 +10,11 @@ linters-settings: simplify: true goimports: local-prefixes: github.com/mattermost/mattermost-plugin-mscalendar - golint: - min-confidence: 0 govet: check-shadowing: true enable-all: true + disable: + - fieldalignment misspell: locale: US @@ -22,27 +22,23 @@ linters: disable-all: true enable: - bodyclose - - deadcode # - errcheck # - goconst - gocritic - gofmt - goimports - - golint # - gosec - gosimple - govet - ineffassign - - interfacer # - misspell - nakedret + - revive - staticcheck - - structcheck - stylecheck - typecheck - unconvert - unused - - varcheck - whitespace issues: diff --git a/server/command/availability.go b/server/command/availability.go index 2fab471c..5bca44d3 100644 --- a/server/command/availability.go +++ b/server/command/availability.go @@ -6,14 +6,14 @@ package command func (c *Command) debugAvailability(parameters ...string) (string, bool, error) { switch { case len(parameters) == 0: - resString, err := c.MSCalendar.Sync(c.Args.UserId) + resString, _, err := c.MSCalendar.Sync(c.Args.UserId) if err != nil { return "", false, err } return resString, false, nil case len(parameters) == 1 && parameters[0] == "all": - resString, err := c.MSCalendar.SyncAll() + resString, _, err := c.MSCalendar.SyncAll() if err != nil { return "", false, err } diff --git a/server/command/command.go b/server/command/command.go index b5c95a37..72755a27 100644 --- a/server/command/command.go +++ b/server/command/command.go @@ -20,11 +20,11 @@ import ( // Handler handles commands type Command struct { + MSCalendar mscalendar.MSCalendar Context *plugin.Context Args *model.CommandArgs - ChannelID string Config *config.Config - MSCalendar mscalendar.MSCalendar + ChannelID string } func getNotConnectedText() string { diff --git a/server/config/config.go b/server/config/config.go index 29404048..93d0e1b8 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -8,25 +8,22 @@ type StoredConfig struct { OAuth2Authority string OAuth2ClientID string OAuth2ClientSecret string - + bot.Config EnableStatusSync bool EnableDailySummary bool - - bot.Config } // Config represents the the metadata handed to all request runners (command, // http). type Config struct { - StoredConfig - + PluginID string BuildDate string BuildHash string BuildHashShort string MattermostSiteHostname string MattermostSiteURL string - PluginID string PluginURL string PluginURLPath string PluginVersion string + StoredConfig } diff --git a/server/jobs/job_manager.go b/server/jobs/job_manager.go index fadea9de..4ae4043e 100644 --- a/server/jobs/job_manager.go +++ b/server/jobs/job_manager.go @@ -16,16 +16,16 @@ import ( ) type JobManager struct { - registeredJobs sync.Map - activeJobs sync.Map env mscalendar.Env papi cluster.JobPluginAPI + registeredJobs sync.Map + activeJobs sync.Map } type RegisteredJob struct { + work func(env mscalendar.Env) id string interval time.Duration - work func(env mscalendar.Env) } var scheduleFunc = func(api cluster.JobPluginAPI, id string, wait cluster.NextWaitInterval, cb func()) (io.Closer, error) { @@ -33,9 +33,9 @@ var scheduleFunc = func(api cluster.JobPluginAPI, id string, wait cluster.NextWa } type activeJob struct { - RegisteredJob ScheduledJob io.Closer Context context.Context + RegisteredJob } func newActiveJob(ctx context.Context, rj RegisteredJob, sched io.Closer) *activeJob { diff --git a/server/jobs/status_sync_job.go b/server/jobs/status_sync_job.go index 66e14c69..fff9040b 100644 --- a/server/jobs/status_sync_job.go +++ b/server/jobs/status_sync_job.go @@ -23,10 +23,10 @@ func NewStatusSyncJob() RegisteredJob { func runSyncJob(env mscalendar.Env) { env.Logger.Debugf("User status sync job beginning") - _, err := mscalendar.New(env, "").SyncAll() + _, syncJobSummary, err := mscalendar.New(env, "").SyncAll() if err != nil { env.Logger.Errorf("Error during user status sync job. err=%v", err) } - env.Logger.Debugf("User status sync job finished") + env.Logger.Debugf("User status sync job finished.\nSummary\nNumber of users processed:- %d\nNumber of users had their status changed:- %d\nNumber of users had errors:- %d", syncJobSummary.NumberOfUsersProcessed, syncJobSummary.NumberOfUsersStatusChanged, syncJobSummary.NumberOfUsersFailedStatusChanged) } diff --git a/server/mscalendar/availability.go b/server/mscalendar/availability.go index b09b3887..2e0acd95 100644 --- a/server/mscalendar/availability.go +++ b/server/mscalendar/availability.go @@ -26,42 +26,50 @@ const ( logTruncateLimit = 5 ) +type StatusSyncJobSummary struct { + NumberOfUsersFailedStatusChanged int + NumberOfUsersStatusChanged int + NumberOfUsersProcessed int +} + type Availability interface { GetCalendarViews(users []*store.User) ([]*remote.ViewCalendarResponse, error) - Sync(mattermostUserID string) (string, error) - SyncAll() (string, error) + Sync(mattermostUserID string) (string, *StatusSyncJobSummary, error) + SyncAll() (string, *StatusSyncJobSummary, error) } -func (m *mscalendar) Sync(mattermostUserID string) (string, error) { +func (m *mscalendar) Sync(mattermostUserID string) (string, *StatusSyncJobSummary, error) { user, err := m.Store.LoadUserFromIndex(mattermostUserID) if err != nil { - return "", err + return "", nil, err } return m.syncUsers(store.UserIndex{user}) } -func (m *mscalendar) SyncAll() (string, error) { +func (m *mscalendar) SyncAll() (string, *StatusSyncJobSummary, error) { err := m.Filter(withSuperuserClient) if err != nil { - return "", errors.Wrap(err, "not able to filter the super user client.") + return "", nil, errors.Wrap(err, "not able to filter the super user client.") } userIndex, err := m.Store.LoadUserIndex() if err != nil { if err.Error() == "not found" { - return "No users found in user index", nil + return "No users found in user index", nil, nil } - return "", errors.Wrap(err, "not able to load users from user index.") + return "", nil, errors.Wrap(err, "not able to load the users from user index.") } return m.syncUsers(userIndex) } -func (m *mscalendar) syncUsers(userIndex store.UserIndex) (string, error) { +func (m *mscalendar) syncUsers(userIndex store.UserIndex) (string, *StatusSyncJobSummary, error) { + syncJobSummary := &StatusSyncJobSummary{} if len(userIndex) == 0 { - return "No connected users found", nil + return "No connected users found", syncJobSummary, nil } + syncJobSummary.NumberOfUsersProcessed = len(userIndex) numberOfLogs := 0 users := []*store.User{} @@ -84,24 +92,27 @@ func (m *mscalendar) syncUsers(userIndex store.UserIndex) (string, error) { } } if len(users) == 0 { - return "No users need to be synced", nil + return "No users need to be synced", syncJobSummary, nil } calendarViews, err := m.GetCalendarViews(users) if err != nil { - return "", errors.Wrap(err, "not able to get calendar views for connected users.") + return "", syncJobSummary, errors.Wrap(err, "not able to get calendar views for connected users.") } if len(calendarViews) == 0 { - return "No calendar views found", nil + return "No calendar views found", syncJobSummary, nil } m.deliverReminders(users, calendarViews) - out, err := m.setUserStatuses(users, calendarViews) + out, numberOfUsersStatusChanged, numberOfUsersFailedStatusChanged, err := m.setUserStatuses(users, calendarViews) if err != nil { - return "", errors.Wrap(err, "error setting the user statuses.") + return "", syncJobSummary, errors.Wrap(err, "error setting the user statuses.") } - return out, nil + syncJobSummary.NumberOfUsersFailedStatusChanged = numberOfUsersFailedStatusChanged + syncJobSummary.NumberOfUsersStatusChanged = numberOfUsersStatusChanged + + return out, syncJobSummary, nil } func (m *mscalendar) deliverReminders(users []*store.User, calendarViews []*remote.ViewCalendarResponse) { @@ -141,8 +152,8 @@ func (m *mscalendar) deliverReminders(users []*store.User, calendarViews []*remo } } -func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remote.ViewCalendarResponse) (string, error) { - numberOfLogs := 0 +func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remote.ViewCalendarResponse) (string, int, int, error) { + numberOfLogs, numberOfUserStatusChange, numberOfUserErrorInStatusChange := 0, 0, 0 toUpdate := []*store.User{} for _, u := range users { if u.Settings.UpdateStatus { @@ -150,7 +161,7 @@ func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remot } } if len(toUpdate) == 0 { - return "No users want their status updated", nil + return "No users want their status updated", numberOfUserStatusChange, numberOfUserErrorInStatusChange, nil } mattermostUserIDs := []string{} @@ -162,7 +173,7 @@ func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remot statuses, appErr := m.PluginAPI.GetMattermostUserStatusesByIds(mattermostUserIDs) if appErr != nil { - return "", errors.Wrap(appErr, "error in getting Mattermost user statuses for connected users.") + return "", numberOfUserStatusChange, numberOfUserErrorInStatusChange, errors.Wrap(appErr, "error in getting Mattermost user statuses for connected users.") } statusMap := map[string]*model.Status{} for _, s := range statuses { @@ -171,6 +182,7 @@ func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remot var res string for _, view := range calendarViews { + isStatusChanged := false user, ok := usersByRemoteID[view.RemoteUserID] if !ok { continue @@ -182,6 +194,7 @@ func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remot m.Logger.Warnf(logTruncateMsg) } numberOfLogs++ + numberOfUserErrorInStatusChange++ continue } @@ -192,27 +205,32 @@ func (m *mscalendar) setUserStatuses(users []*store.User, calendarViews []*remot } var err error - res, err = m.setStatusFromCalendarView(user, status, view) + res, isStatusChanged, err = m.setStatusFromCalendarView(user, status, view) if err != nil { if numberOfLogs < logTruncateLimit { m.Logger.Warnf("Error setting user %s status. err=%v", user.MattermostUserID, err) } else if numberOfLogs == logTruncateLimit { m.Logger.Warnf(logTruncateMsg) } + numberOfLogs++ + numberOfUserErrorInStatusChange++ + } + if isStatusChanged { + numberOfUserStatusChange++ } - numberOfLogs++ } if res != "" { - return res, nil + return res, numberOfUserStatusChange, numberOfUserErrorInStatusChange, nil } - return utils.JSONBlock(calendarViews), nil + return utils.JSONBlock(calendarViews), numberOfUserStatusChange, numberOfUserErrorInStatusChange, nil } -func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.Status, res *remote.ViewCalendarResponse) (string, error) { +func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.Status, res *remote.ViewCalendarResponse) (string, bool, error) { + isStatusChanged := false currentStatus := status.Status if currentStatus == model.STATUS_OFFLINE && !user.Settings.GetConfirmation { - return "User offline and does not want status change confirmations. No status change", nil + return "User offline and does not want status change confirmations. No status change", isStatusChanged, nil } events := filterBusyEvents(res.Events) @@ -222,7 +240,7 @@ func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.S } if len(user.ActiveEvents) == 0 && len(events) == 0 { - return "No events in local or remote. No status change.", nil + return "No events in local or remote. No status change.", isStatusChanged, nil } if len(user.ActiveEvents) > 0 && len(events) == 0 { @@ -234,15 +252,16 @@ func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.S } err := m.setStatusOrAskUser(user, status, events, true) if err != nil { - return "", errors.Wrapf(err, "error in setting user status for user %s", user.MattermostUserID) + return "", isStatusChanged, errors.Wrapf(err, "error in setting user status for user %s", user.MattermostUserID) } + isStatusChanged = true } err := m.Store.StoreUserActiveEvents(user.MattermostUserID, []string{}) if err != nil { - return "", errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) + return "", isStatusChanged, errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) } - return message, nil + return message, isStatusChanged, nil } remoteHashes := []string{} @@ -264,19 +283,20 @@ func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.S m.Store.StoreUser(user) err = m.Store.StoreUserActiveEvents(user.MattermostUserID, remoteHashes) if err != nil { - return "", errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) + return "", isStatusChanged, errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) } - return "User was already marked as busy. No status change.", nil + return "User was already marked as busy. No status change.", isStatusChanged, nil } err = m.setStatusOrAskUser(user, status, events, false) if err != nil { - return "", errors.Wrapf(err, "error in setting user status for user %s", user.MattermostUserID) + return "", isStatusChanged, errors.Wrapf(err, "error in setting user status for user %s", user.MattermostUserID) } + isStatusChanged = true err = m.Store.StoreUserActiveEvents(user.MattermostUserID, remoteHashes) if err != nil { - return "", errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) + return "", isStatusChanged, errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) } - return fmt.Sprintf("User was free, but is now busy (%s). Set status to busy.", busyStatus), nil + return fmt.Sprintf("User was free, but is now busy (%s). Set status to busy.", busyStatus), isStatusChanged, nil } newEventExists := false @@ -295,24 +315,25 @@ func (m *mscalendar) setStatusFromCalendarView(user *store.User, status *model.S } if !newEventExists { - return fmt.Sprintf("No change in active events. Total number of events: %d", len(events)), nil + return fmt.Sprintf("No change in active events. Total number of events: %d", len(events)), isStatusChanged, nil } message := "User is already busy. No status change." if currentStatus != busyStatus { err := m.setStatusOrAskUser(user, status, events, false) if err != nil { - return "", errors.Wrapf(err, "error in setting user status for user %s", user.MattermostUserID) + return "", isStatusChanged, errors.Wrapf(err, "error in setting user status for user %s", user.MattermostUserID) } + isStatusChanged = true message = fmt.Sprintf("User was free, but is now busy. Set status to busy (%s).", busyStatus) } err := m.Store.StoreUserActiveEvents(user.MattermostUserID, remoteHashes) if err != nil { - return "", errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) + return "", isStatusChanged, errors.Wrapf(err, "error in storing active events for user %s", user.MattermostUserID) } - return message, nil + return message, isStatusChanged, nil } // setStatusOrAskUser to which status change, and whether it should update the status automatically or ask the user. diff --git a/server/mscalendar/availability_test.go b/server/mscalendar/availability_test.go index 159509e0..831d6acf 100644 --- a/server/mscalendar/availability_test.go +++ b/server/mscalendar/availability_test.go @@ -27,13 +27,13 @@ func TestSyncStatusAll(t *testing.T) { busyEvent := &remote.Event{ICalUID: "event_id", Start: remote.NewDateTime(moment, "UTC"), ShowAs: "busy"} for name, tc := range map[string]struct { - remoteEvents []*remote.Event apiError *remote.APIError - activeEvents []string currentStatus string - currentStatusManual bool newStatus string + remoteEvents []*remote.Event + activeEvents []string eventsToStore []string + currentStatusManual bool shouldLogError bool getConfirmation bool }{ @@ -161,7 +161,7 @@ func TestSyncStatusAll(t *testing.T) { } m := New(env, "") - res, err := m.SyncAll() + res, _, err := m.SyncAll() require.Nil(t, err) require.NotEmpty(t, res) }) @@ -170,8 +170,8 @@ func TestSyncStatusAll(t *testing.T) { func TestSyncStatusUserConfig(t *testing.T) { for name, tc := range map[string]struct { - settings store.Settings runAssertions func(deps *Dependencies, client remote.Client) + settings store.Settings }{ "UpdateStatus disabled": { settings: store.Settings{ @@ -223,7 +223,7 @@ func TestSyncStatusUserConfig(t *testing.T) { tc.runAssertions(env.Dependencies, client) mscalendar := New(env, "") - _, err := mscalendar.SyncAll() + _, _, err := mscalendar.SyncAll() require.Nil(t, err) }) } @@ -231,9 +231,9 @@ func TestSyncStatusUserConfig(t *testing.T) { func TestReminders(t *testing.T) { for name, tc := range map[string]struct { + apiError *remote.APIError remoteEvents []*remote.Event numReminders int - apiError *remote.APIError shouldLogError bool }{ "Most common case, no remote events. No reminder.": { @@ -321,7 +321,7 @@ func TestReminders(t *testing.T) { } m := New(env, "") - res, err := m.SyncAll() + res, _, err := m.SyncAll() require.Nil(t, err) require.NotEmpty(t, res) }) diff --git a/server/mscalendar/daily_summary_test.go b/server/mscalendar/daily_summary_test.go index 76274051..adf3d92b 100644 --- a/server/mscalendar/daily_summary_test.go +++ b/server/mscalendar/daily_summary_test.go @@ -21,9 +21,9 @@ import ( func TestProcessAllDailySummary(t *testing.T) { for _, tc := range []struct { + runAssertions func(deps *Dependencies, client remote.Client) name string err string - runAssertions func(deps *Dependencies, client remote.Client) }{ { name: "Error fetching index", @@ -210,9 +210,9 @@ Wednesday February 12 func TestShouldPostDailySummary(t *testing.T) { tests := []struct { name string - enabled bool postTime string timeZone string + enabled bool shouldRun bool shouldError bool }{ diff --git a/server/mscalendar/mock_mscalendar/mock_mscalendar.go b/server/mscalendar/mock_mscalendar/mock_mscalendar.go index 8d6bc66c..05e380aa 100644 --- a/server/mscalendar/mock_mscalendar/mock_mscalendar.go +++ b/server/mscalendar/mock_mscalendar/mock_mscalendar.go @@ -471,12 +471,13 @@ func (mr *MockMSCalendarMockRecorder) SetDailySummaryPostTime(arg0, arg1 interfa } // Sync mocks base method. -func (m *MockMSCalendar) Sync(arg0 string) (string, error) { +func (m *MockMSCalendar) Sync(arg0 string) (string, *mscalendar.StatusSyncJobSummary, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Sync", arg0) ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(*mscalendar.StatusSyncJobSummary) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // Sync indicates an expected call of Sync. @@ -486,12 +487,13 @@ func (mr *MockMSCalendarMockRecorder) Sync(arg0 interface{}) *gomock.Call { } // SyncAll mocks base method. -func (m *MockMSCalendar) SyncAll() (string, error) { +func (m *MockMSCalendar) SyncAll() (string, *mscalendar.StatusSyncJobSummary, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SyncAll") ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(*mscalendar.StatusSyncJobSummary) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // SyncAll indicates an expected call of SyncAll. diff --git a/server/mscalendar/notification.go b/server/mscalendar/notification.go index 480b0178..26b5a9a7 100644 --- a/server/mscalendar/notification.go +++ b/server/mscalendar/notification.go @@ -48,9 +48,9 @@ const ( ResponseNone = "notResponded" ) -var importantNotificationChanges []string = []string{FieldSubject, FieldWhen} +var importantNotificationChanges = []string{FieldSubject, FieldWhen} -var notificationFieldOrder []string = []string{ +var notificationFieldOrder = []string{ FieldWhen, FieldLocation, FieldAttendees, @@ -266,7 +266,9 @@ func (processor *notificationProcessor) updatedEventSlackAttachment(n *remote.No return false, nil } - allChanges := append(added, updated...) + var allChanges []string + allChanges = append(allChanges, added...) + allChanges = append(allChanges, updated...) allChanges = append(allChanges, deleted...) hasImportantChanges := false diff --git a/server/mscalendar/notification_test.go b/server/mscalendar/notification_test.go index e26bb48d..d413c46a 100644 --- a/server/mscalendar/notification_test.go +++ b/server/mscalendar/notification_test.go @@ -92,10 +92,10 @@ func newTestNotification(clientState string, recommendRenew bool) *remote.Notifi func TestProcessNotification(t *testing.T) { tcs := []struct { - name string - expectedError string notification *remote.Notification priorEvent *remote.Event + name string + expectedError string }{ { name: "incoming ClientState matches stored ClientState", diff --git a/server/mscalendar/oauth2_test.go b/server/mscalendar/oauth2_test.go index 17e1caba..9c0630d2 100644 --- a/server/mscalendar/oauth2_test.go +++ b/server/mscalendar/oauth2_test.go @@ -78,11 +78,11 @@ func TestInitOAuth2(t *testing.T) { defer ctrl.Finish() tcs := []struct { + setup func(dependencies *Dependencies) name string mattermostUserID string - setup func(dependencies *Dependencies) - expectError bool expectURL string + expectError bool }{ { name: "MM user already connected", diff --git a/server/mscalendar/settings_daily_summary.go b/server/mscalendar/settings_daily_summary.go index 2b13c4c7..2a1db36f 100644 --- a/server/mscalendar/settings_daily_summary.go +++ b/server/mscalendar/settings_daily_summary.go @@ -12,15 +12,15 @@ import ( ) type dailySummarySetting struct { + store settingspanel.SettingStore + getTimezone func(userID string) (string, error) title string + dependsOn string description string id string - dependsOn string optionsH []string optionsM []string optionsAPM []string - store settingspanel.SettingStore - getTimezone func(userID string) (string, error) } func NewDailySummarySetting(inStore settingspanel.SettingStore, getTimezone func(userID string) (string, error)) settingspanel.Setting { diff --git a/server/mscalendar/settings_notifications.go b/server/mscalendar/settings_notifications.go index ef453e73..cbb501ff 100644 --- a/server/mscalendar/settings_notifications.go +++ b/server/mscalendar/settings_notifications.go @@ -9,11 +9,11 @@ import ( ) type notificationSetting struct { + getCal func(string) MSCalendar title string description string id string dependsOn string - getCal func(string) MSCalendar } func NewNotificationsSetting(getCal func(string) MSCalendar) settingspanel.Setting { diff --git a/server/mscalendar/user.go b/server/mscalendar/user.go index 3501d862..48a224d6 100644 --- a/server/mscalendar/user.go +++ b/server/mscalendar/user.go @@ -24,9 +24,9 @@ type Users interface { } type User struct { - MattermostUserID string *store.User - MattermostUser *model.User + MattermostUser *model.User + MattermostUserID string } func NewUser(mattermostUserID string) *User { @@ -61,7 +61,7 @@ func (m *mscalendar) ExpandRemoteUser(user *User) error { if user.User == nil { storedUser, err := m.Store.LoadUser(user.MattermostUserID) if err != nil { - return errors.Wrap(err, "It looks like your Mattermost account is not connected to a Microsoft account. Please connect your account using `/mscalendar connect`.") + return errors.Wrap(err, "It looks like your Mattermost account is not connected to a Microsoft account. Please connect your account using `/mscalendar connect`.") //nolint:revive } user.User = storedUser } diff --git a/server/mscalendar/welcome_flow.go b/server/mscalendar/welcome_flow.go index 98f25750..bffc5e68 100644 --- a/server/mscalendar/welcome_flow.go +++ b/server/mscalendar/welcome_flow.go @@ -7,10 +7,10 @@ import ( ) type WelcomeFlow struct { - steps []flow.Step - url string controller bot.FlowController onFlowDone func(userID string) + url string + steps []flow.Step } func NewWelcomeFlow(bot bot.FlowController, welcomer Welcomer) *WelcomeFlow { diff --git a/server/remote/calendar.go b/server/remote/calendar.go index 093e16a3..0d3ea0b9 100644 --- a/server/remote/calendar.go +++ b/server/remote/calendar.go @@ -8,21 +8,21 @@ import ( ) type Calendar struct { + Owner *User `json:"owner,omitempty"` ID string `json:"id"` Name string `json:"name,omitempty"` Events []Event `json:"events,omitempty"` CalendarView []Event `json:"calendarView,omitempty"` - Owner *User `json:"owner,omitempty"` } type ViewCalendarParams struct { - RemoteUserID string StartTime time.Time EndTime time.Time + RemoteUserID string } type ViewCalendarResponse struct { + Error *APIError RemoteUserID string Events []*Event - Error *APIError } diff --git a/server/remote/event.go b/server/remote/event.go index af4ec4d1..30d68c98 100644 --- a/server/remote/event.go +++ b/server/remote/event.go @@ -4,25 +4,25 @@ package remote type Event struct { - ID string `json:"id,omitempty"` + Start *DateTime `json:"start,omitempty"` + Location *Location `json:"location,omitempty"` + End *DateTime `json:"end,omitempty"` + Organizer *Attendee `json:"organizer,omitempty"` + Body *ItemBody `json:"Body,omitempty"` + ResponseStatus *EventResponseStatus `json:"responseStatus,omitempty"` + Importance string `json:"importance,omitempty"` ICalUID string `json:"iCalUId,omitempty"` Subject string `json:"subject,omitempty"` BodyPreview string `json:"bodyPreview,omitempty"` - Body *ItemBody `json:"Body,omitempty"` - Importance string `json:"importance,omitempty"` - IsAllDay bool `json:"isAllDay,omitempty"` - IsCancelled bool `json:"isCancelled,omitempty"` - IsOrganizer bool `json:"isOrganizer,omitempty"` - ResponseRequested bool `json:"responseRequested,omitempty"` ShowAs string `json:"showAs,omitempty"` Weblink string `json:"weblink,omitempty"` - Start *DateTime `json:"start,omitempty"` - End *DateTime `json:"end,omitempty"` - ReminderMinutesBeforeStart int `json:"reminderMinutesBeforeStart,omitempty"` - Location *Location `json:"location,omitempty"` - ResponseStatus *EventResponseStatus `json:"responseStatus,omitempty"` + ID string `json:"id,omitempty"` Attendees []*Attendee `json:"attendees,omitempty"` - Organizer *Attendee `json:"organizer,omitempty"` + ReminderMinutesBeforeStart int `json:"reminderMinutesBeforeStart,omitempty"` + IsOrganizer bool `json:"isOrganizer,omitempty"` + IsCancelled bool `json:"isCancelled,omitempty"` + IsAllDay bool `json:"isAllDay,omitempty"` + ResponseRequested bool `json:"responseRequested,omitempty"` } type ItemBody struct { @@ -56,7 +56,7 @@ type Coordinates struct { } type Attendee struct { - Type string `json:"type,omitempty"` Status *EventResponseStatus `json:"status,omitempty"` EmailAddress *EmailAddress `json:"emailAddress,omitempty"` + Type string `json:"type,omitempty"` } diff --git a/server/remote/meeting.go b/server/remote/meeting.go index 02138105..baaf4c82 100644 --- a/server/remote/meeting.go +++ b/server/remote/meeting.go @@ -6,14 +6,14 @@ package remote import "time" type FindMeetingTimesParameters struct { - Attendees []Attendee `json:"attendees,omitempty"` + ReturnSuggestionReasons *bool `json:"returnSuggestionReasons,omitempty"` LocationConstraint *LocationConstraint `json:"locationConstraint,omitempty"` TimeConstraint *TimeConstraint `json:"timeConstraint,omitempty"` MeetingDuration *time.Duration `json:"meetingDuration,omitempty"` MaxCandidates *int `json:"maxCandidates,omitempty"` IsOrganizerOptional *bool `json:"isOrganizerOptional,omitempty"` - ReturnSuggestionReasons *bool `json:"returnSuggestionReasons,omitempty"` MinimumAttendeePercentage *float64 `json:"minimumAttendeePercentage,omitempty"` + Attendees []Attendee `json:"attendees,omitempty"` } type TimeConstraint struct { @@ -21,13 +21,13 @@ type TimeConstraint struct { TimeSlots []TimeSlot `json:"timeSlots,omitempty"` } type MeetingTimeSuggestion struct { - AttendeeAvailability []*AttendeeAvailability - Confidence float32 `json:"confidence"` - Locations []*Location MeetingTimeSlot *TimeSlot - Order int32 `json:"order"` - OrganizerAvailability string `json:"organizerAvailability"` SuggestionReason string `json:"suggestionReason"` + OrganizerAvailability string `json:"organizerAvailability"` + Locations []*Location + AttendeeAvailability []*AttendeeAvailability + Confidence float32 `json:"confidence"` + Order int32 `json:"order"` } type AttendeeAvailability struct { @@ -36,8 +36,8 @@ type AttendeeAvailability struct { } type MeetingTimeSuggestionResults struct { - MeetingTimeSuggestions []*MeetingTimeSuggestion `json:"meetingTimeSuggestions"` EmptySuggestionReason string `json:"emptySuggestionReason"` + MeetingTimeSuggestions []*MeetingTimeSuggestion `json:"meetingTimeSuggestions"` } type TimeSlot struct { @@ -46,9 +46,9 @@ type TimeSlot struct { } type LocationConstraint struct { - Locations []LocationConstraintItem `json:"locations,omitempty"` IsRequired *bool `json:"isRequired,omitempty"` SuggestLocation *bool `json:"suggestLocation,omitempty"` + Locations []LocationConstraintItem `json:"locations,omitempty"` } type LocationConstraintItem struct { diff --git a/server/remote/msgraph/batch_request.go b/server/remote/msgraph/batch_request.go index ce8ef311..288c6743 100644 --- a/server/remote/msgraph/batch_request.go +++ b/server/remote/msgraph/batch_request.go @@ -10,11 +10,11 @@ import ( const maxNumRequestsPerBatch = 20 type singleRequest struct { + Body interface{} `json:"body"` + Headers map[string]string `json:"headers"` ID string `json:"id"` URL string `json:"url"` Method string `json:"method"` - Body interface{} `json:"body"` - Headers map[string]string `json:"headers"` } type fullBatchRequest struct { diff --git a/server/remote/msgraph/get_default_calendar_view.go b/server/remote/msgraph/get_default_calendar_view.go index 10e65a97..3faa83a4 100644 --- a/server/remote/msgraph/get_default_calendar_view.go +++ b/server/remote/msgraph/get_default_calendar_view.go @@ -14,15 +14,15 @@ import ( ) type calendarViewResponse struct { - Value []*remote.Event `json:"value,omitempty"` Error *remote.APIError `json:"error,omitempty"` + Value []*remote.Event `json:"value,omitempty"` } type calendarViewSingleResponse struct { + Headers map[string]string `json:"headers"` ID string `json:"id"` - Status int `json:"status"` Body calendarViewResponse `json:"body"` - Headers map[string]string `json:"headers"` + Status int `json:"status"` } type calendarViewBatchResponse struct { diff --git a/server/remote/msgraph/get_schedule_batched.go b/server/remote/msgraph/get_schedule_batched.go index 574e763a..3b0c9bf7 100644 --- a/server/remote/msgraph/get_schedule_batched.go +++ b/server/remote/msgraph/get_schedule_batched.go @@ -9,15 +9,15 @@ import ( ) type getScheduleResponse struct { - Value []*remote.ScheduleInformation `json:"value,omitempty"` Error *remote.APIError `json:"error,omitempty"` + Value []*remote.ScheduleInformation `json:"value,omitempty"` } type getScheduleSingleResponse struct { + Headers map[string]string `json:"headers"` ID string `json:"id"` - Status int `json:"status"` Body getScheduleResponse `json:"body"` - Headers map[string]string `json:"headers"` + Status int `json:"status"` } type getScheduleBatchResponse struct { @@ -25,13 +25,13 @@ type getScheduleBatchResponse struct { } type getScheduleRequestParams struct { - // List of emails of users that we want to check - Schedules []string `json:"schedules"` - // Overall start and end of entire search window StartTime *remote.DateTime `json:"startTime"` EndTime *remote.DateTime `json:"endTime"` + // List of emails of users that we want to check + Schedules []string `json:"schedules"` + /* Size of each chunk of time we want to check This can be equal to end - start if we want, or we can get more granular results by making it shorter. diff --git a/server/remote/msgraph/get_super_user_token.go b/server/remote/msgraph/get_super_user_token.go index d9c7a693..f3a1968e 100644 --- a/server/remote/msgraph/get_super_user_token.go +++ b/server/remote/msgraph/get_super_user_token.go @@ -12,8 +12,8 @@ import ( type AuthResponse struct { TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` } func (c *client) GetSuperuserToken() (string, error) { diff --git a/server/remote/notification.go b/server/remote/notification.go index 8c64d0cb..8f1eb3b3 100644 --- a/server/remote/notification.go +++ b/server/remote/notification.go @@ -4,12 +4,27 @@ package remote type Notification struct { + Webhook interface{} + + // Notification data + Subscription *Subscription + SubscriptionCreator *User + Event *Event + + // ClientState from the webhook. The handler is to validate against its own + // persistent secret. + ClientState string + // Notification type ChangeType string // The (remote) subscription ID the notification is for SubscriptionID string + // Remote-specific data: full raw JSON of the webhook, and the decoded + // backend-specific struct. + WebhookRawData []byte + // Set if subscription renewal is recommended. The date/time logic is // internal to the remote implementation. The handler is to call // RenewSubscription() as applicable, with the appropriate user credentials. @@ -19,18 +34,4 @@ type Notification struct { // handler is to call GetNofiticationData(), with the appropriate user // credentials. IsBare bool - - // ClientState from the webhook. The handler is to validate against its own - // persistent secret. - ClientState string - - // Remote-specific data: full raw JSON of the webhook, and the decoded - // backend-specific struct. - WebhookRawData []byte - Webhook interface{} - - // Notification data - Subscription *Subscription - SubscriptionCreator *User - Event *Event } diff --git a/server/remote/schedule.go b/server/remote/schedule.go index ef2db7ee..144125b6 100644 --- a/server/remote/schedule.go +++ b/server/remote/schedule.go @@ -47,10 +47,10 @@ type ScheduleUserInfo struct { } type ScheduleItem struct { - IsPrivate bool + Start *DateTime + End *DateTime Status string Subject string Location string - Start *DateTime - End *DateTime + IsPrivate bool } diff --git a/server/remote/user.go b/server/remote/user.go index a268b99c..9d1c58c0 100644 --- a/server/remote/user.go +++ b/server/remote/user.go @@ -11,12 +11,12 @@ type User struct { } type WorkingHours struct { - DaysOfWeek []string `json:"daysOfWeek"` - StartTime string `json:"startTime"` - EndTime string `json:"endTime"` - TimeZone struct { + StartTime string `json:"startTime"` + EndTime string `json:"endTime"` + TimeZone struct { Name string `json:"name"` } + DaysOfWeek []string `json:"daysOfWeek"` } type MailboxSettings struct { diff --git a/server/store/event_store.go b/server/store/event_store.go index 55deca27..f983d8cb 100644 --- a/server/store/event_store.go +++ b/server/store/event_store.go @@ -19,8 +19,8 @@ const ttlAfterEventEnd = 30 * 24 * time.Hour // 30 days const defaultEventTTL = 30 * 24 * time.Hour // 30 days type Event struct { - PluginVersion string Remote *remote.Event + PluginVersion string } type EventStore interface { diff --git a/server/store/user_store.go b/server/store/user_store.go index 662c5e97..0149b7cc 100644 --- a/server/store/user_store.go +++ b/server/store/user_store.go @@ -35,30 +35,30 @@ type UserShort struct { } type User struct { - PluginVersion string + Settings Settings `json:"mattermostSettings,omitempty"` Remote *remote.User - MattermostUserID string OAuth2Token *oauth2.Token - Settings Settings `json:"mattermostSettings,omitempty"` - ActiveEvents []string `json:"events"` + PluginVersion string + MattermostUserID string LastStatus string WelcomeFlowStatus WelcomeFlowStatus `json:"mattermostFlags,omitempty"` + ActiveEvents []string `json:"events"` } type Settings struct { + DailySummary *DailySummaryUserSettings EventSubscriptionID string UpdateStatus bool GetConfirmation bool ReceiveReminders bool ReceiveNotificationsDuringMeeting bool - DailySummary *DailySummaryUserSettings } type DailySummaryUserSettings struct { - Enable bool `json:"enable"` PostTime string `json:"post_time"` // Kitchen format, i.e. 8:30AM Timezone string `json:"tz"` // Timezone in MSCal when PostTime is set/updated LastPostTime string `json:"last_post_time"` + Enable bool `json:"enable"` } type WelcomeFlowStatus struct { @@ -207,7 +207,10 @@ func (s *pluginStore) StoreUserInIndex(user *User) error { for i, u := range userIndex { if u.MattermostUserID == user.MattermostUserID && u.RemoteID == user.Remote.ID { - result := append(userIndex[:i], newUser) + var result UserIndex + result = append(result, userIndex[:i]...) + result = append(result, newUser) + return append(result, userIndex[i+1:]...), nil } } diff --git a/server/utils/bot/bot.go b/server/utils/bot/bot.go index a998b6e4..7dd53cbd 100644 --- a/server/utils/bot/bot.go +++ b/server/utils/bot/bot.go @@ -25,16 +25,15 @@ type Bot interface { } type bot struct { - Config pluginAPI plugin.API pluginHelpers plugin.Helpers - mattermostUserID string - displayName string + flow flow.Flow + flowStore flow.Store logContext LogContext pluginURL string - - flow flow.Flow - flowStore flow.Store + mattermostUserID string + displayName string + Config } func New(api plugin.API, helpers plugin.Helpers, pluginURL string) Bot { diff --git a/server/utils/byte_size_test.go b/server/utils/byte_size_test.go index dd86621c..c08c8765 100644 --- a/server/utils/byte_size_test.go +++ b/server/utils/byte_size_test.go @@ -62,32 +62,32 @@ func TestParseByteSize(t *testing.T) { func TestByteSizeString(t *testing.T) { tests := []struct { - n ByteSize want string + n ByteSize }{ - {0, "0"}, - {1, "1b"}, - {999, "999b"}, - {1000, "1,000b"}, - {1023, "1,023b"}, - {1024, "1Kb"}, - {12345, "12.1Kb"}, - {12851, "12.5Kb"}, // 12.54980 - {12852, "12.6Kb"}, // 12.55078 - {123456, "120.6Kb"}, - {1234567, "1.2Mb"}, - {12345678, "11.8Mb"}, - {123456789, "117.7Mb"}, - {1234567890, "1.1Gb"}, - {12345678900, "11.5Gb"}, - {123456789000, "115Gb"}, - {1234567890000, "1.1Tb"}, - {12345678900000, "11.2Tb"}, - {123456789000000, "112.3Tb"}, - {1234567890000000, "1,122.8Tb"}, - {12345678900000000, "11,228.3Tb"}, - {123456789000000000, "112,283.3Tb"}, - {1234567890000000000, "n/a"}, + {"0", 0}, + {"1b", 1}, + {"999b", 999}, + {"1,000b", 1000}, + {"1,023b", 1023}, + {"1Kb", 1024}, + {"12.1Kb", 12345}, + {"12.5Kb", 12851}, // 12.54980 + {"12.6Kb", 12852}, // 12.55078 + {"120.6Kb", 123456}, + {"1.2Mb", 1234567}, + {"11.8Mb", 12345678}, + {"117.7Mb", 123456789}, + {"1.1Gb", 1234567890}, + {"11.5Gb", 12345678900}, + {"115Gb", 123456789000}, + {"1.1Tb", 1234567890000}, + {"11.2Tb", 12345678900000}, + {"112.3Tb", 123456789000000}, + {"1,122.8Tb", 1234567890000000}, + {"11,228.3Tb", 12345678900000000}, + {"112,283.3Tb", 123456789000000000}, + {"n/a", 1234567890000000000}, } for _, tt := range tests { t.Run(fmt.Sprintf("%d", tt.n), func(t *testing.T) { diff --git a/server/utils/httputils/limited_readcloser.go b/server/utils/httputils/limited_readcloser.go index d0f19a37..a1006d2e 100644 --- a/server/utils/httputils/limited_readcloser.go +++ b/server/utils/httputils/limited_readcloser.go @@ -11,9 +11,9 @@ import ( type LimitReadCloser struct { ReadCloser io.ReadCloser + OnClose func(*LimitReadCloser) error TotalRead utils.ByteSize Limit utils.ByteSize - OnClose func(*LimitReadCloser) error } func (r *LimitReadCloser) Read(data []byte) (int, error) { diff --git a/server/utils/settingspanel/bool_setting.go b/server/utils/settingspanel/bool_setting.go index fd0b76fe..189f8a00 100644 --- a/server/utils/settingspanel/bool_setting.go +++ b/server/utils/settingspanel/bool_setting.go @@ -8,11 +8,11 @@ import ( ) type boolSetting struct { + store SettingStore title string description string id string dependsOn string - store SettingStore } func NewBoolSetting(id string, title string, description string, dependsOn string, store SettingStore) Setting { diff --git a/server/utils/settingspanel/option_setting.go b/server/utils/settingspanel/option_setting.go index 544abe04..4c197ce2 100644 --- a/server/utils/settingspanel/option_setting.go +++ b/server/utils/settingspanel/option_setting.go @@ -8,12 +8,12 @@ import ( ) type optionSetting struct { + store SettingStore title string description string id string dependsOn string options []string - store SettingStore } func NewOptionSetting(id string, title string, description string, dependsOn string, options []string, store SettingStore) Setting { diff --git a/server/utils/settingspanel/read_only_setting.go b/server/utils/settingspanel/read_only_setting.go index fc8bf2a4..448294bf 100644 --- a/server/utils/settingspanel/read_only_setting.go +++ b/server/utils/settingspanel/read_only_setting.go @@ -8,11 +8,11 @@ import ( ) type readOnlySetting struct { + store SettingStore title string description string id string dependsOn string - store SettingStore } func NewReadOnlySetting(id string, title string, description string, dependsOn string, store SettingStore) Setting { diff --git a/server/utils/settingspanel/settings.go b/server/utils/settingspanel/settings.go index d3aa1334..2e13e7a8 100644 --- a/server/utils/settingspanel/settings.go +++ b/server/utils/settingspanel/settings.go @@ -41,13 +41,13 @@ type PanelStore interface { } type panel struct { - settings map[string]Setting - settingKeys []string poster bot.Poster logger bot.Logger store PanelStore + settings map[string]Setting settingHandler string pluginURL string + settingKeys []string } func NewSettingsPanel(settings []Setting, poster bot.Poster, logger bot.Logger, store PanelStore, settingHandler, pluginURL string) Panel { diff --git a/server/utils/telemetry/tracker.go b/server/utils/telemetry/tracker.go index 127c205a..27e8f7f2 100644 --- a/server/utils/telemetry/tracker.go +++ b/server/utils/telemetry/tracker.go @@ -13,20 +13,20 @@ type Client interface { } type Track struct { + Properties map[string]interface{} UserID string Event string - Properties map[string]interface{} } type tracker struct { client Client - diagnosticID string + logger bot.Logger serverVersion string pluginID string pluginVersion string telemetryShortName string + diagnosticID string enabled bool - logger bot.Logger } func NewTracker(c Client, diagnosticID, serverVersion, pluginID, pluginVersion, telemetryShortName string, enableDiagnostics bool, logger bot.Logger) Tracker {