diff --git a/README.md b/README.md index 311a14e..188b32e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Initial development as part of [Mattermost Hackathon 2019](https://github.com/ma #### Meeting Settings Configuration -The meeting settings for each channel can be configured in the Channel Header Dropwdown (supported in [this WebApp branch](https://github.com/mattermost/mattermost-webapp/tree/MM-19902)) +The meeting settings for each channel can be configured in the Channel Header Dropdown. ![channel_header_menu](./assets/channelHeaderDropdown.png) @@ -26,7 +26,7 @@ Meeting settings include: ``` /agenda queue [next-week] message ``` -Creates a post for the user with the given `message` for the next meeting date with the configured hashtag format preceding it. +Creates a post for the user with the given `message` for the next meeting date. The configured hashtag will precede the `message`. If `next-week` is indicated (optional), it will use the date of the meeting in the next calendar week. ![post_example](./assets/postExample.png) diff --git a/server/command.go b/server/command.go index 73e80d1..d910a4c 100644 --- a/server/command.go +++ b/server/command.go @@ -15,12 +15,11 @@ import ( const ( commandTriggerAgenda = "agenda" - WS_EVENT_LIST = "list" + wsEventList = "list" ) func (p *Plugin) registerCommands() error { if err := p.API.RegisterCommand(&model.Command{ - Trigger: commandTriggerAgenda, AutoComplete: true, AutoCompleteHint: "[command]", @@ -32,7 +31,7 @@ func (p *Plugin) registerCommands() error { return nil } -// ExecuteCommand +// ExecuteCommand executes a command that has been previously registered via the RegisterCommand func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) { split := strings.Fields(args.Command) command := split[0] @@ -76,16 +75,17 @@ func (p *Plugin) executeCommandList(args *model.CommandArgs) *model.CommandRespo split := strings.Fields(args.Command) nextWeek := len(split) > 2 && split[2] == "next-week" - hashtag, error := p.GenerateHashtag(args.ChannelId, nextWeek) - if error != nil { + hashtag, err := p.GenerateHashtag(args.ChannelId, nextWeek) + if err != nil { return &model.CommandResponse{ ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: fmt.Sprintf("Error calculating hashtags"), } } + //TODO need to understand this p.API.PublishWebSocketEvent( - WS_EVENT_LIST, + wsEventList, map[string]interface{}{ "hashtag": hashtag, }, @@ -136,7 +136,7 @@ func (p *Plugin) executeCommandSetting(args *model.CommandArgs) *model.CommandRe } else { return &model.CommandResponse{ ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, - Text: fmt.Sprintf("Unknow setting " + field), + Text: fmt.Sprintf("Unknown setting " + field), } } @@ -171,32 +171,31 @@ func (p *Plugin) executeCommandQueue(args *model.CommandArgs) *model.CommandResp message = strings.Join(split[3:], " ") } - hashtag, error := p.GenerateHashtag(args.ChannelId, nextWeek) - if error != nil { + hashtag, err := p.GenerateHashtag(args.ChannelId, nextWeek) + if err != nil { return &model.CommandResponse{ ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: fmt.Sprintf("Error calculating hashtags"), } } - itemsQueued, appError := p.API.SearchPostsInTeam(args.TeamId, []*model.SearchParams{{Terms: hashtag, IsHashtag: true}}) - - if appError != nil { + itemsQueued, appErr := p.API.SearchPostsInTeam(args.TeamId, []*model.SearchParams{{Terms: hashtag, IsHashtag: true}}) + if appErr != nil { return &model.CommandResponse{ ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: fmt.Sprintf("Error getting user"), } } - _, err := p.API.CreatePost(&model.Post{ + _, appErr = p.API.CreatePost(&model.Post{ UserId: args.UserId, ChannelId: args.ChannelId, Message: fmt.Sprintf("#### %v %v) %v", hashtag, len(itemsQueued)+1, message), }) - if err != nil { + if appErr != nil { return &model.CommandResponse{ ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, - Text: fmt.Sprintf("Error creating post: " + err.Message), + Text: fmt.Sprintf("Error creating post: " + appErr.Message), } } diff --git a/server/meeting.go b/server/meeting.go index 25fa5d1..58e8c77 100644 --- a/server/meeting.go +++ b/server/meeting.go @@ -6,22 +6,24 @@ import ( "time" ) +// Meeting represents a meeting agenda type Meeting struct { - ChannelId string `json:"channelId"` + ChannelID string `json:"channelId"` Schedule time.Weekday `json:"schedule"` HashtagFormat string `json:"hashtagFormat"` //Default: Jan02 } -func (p *Plugin) GetMeeting(channelId string) (*Meeting, error) { +// GetMeeting returns a meeting +func (p *Plugin) GetMeeting(channelID string) (*Meeting, error) { - meettingBytes, appErr := p.API.KVGet(channelId) + meetingBytes, appErr := p.API.KVGet(channelID) if appErr != nil { return nil, appErr } var meeting *Meeting - if meettingBytes != nil { - if err := json.Unmarshal(meettingBytes, &meeting); err != nil { + if meetingBytes != nil { + if err := json.Unmarshal(meetingBytes, &meeting); err != nil { return nil, err } } else { @@ -29,13 +31,14 @@ func (p *Plugin) GetMeeting(channelId string) (*Meeting, error) { meeting = &Meeting{ Schedule: time.Thursday, HashtagFormat: "Jan02", - ChannelId: channelId, + ChannelID: channelID, } } return meeting, nil } +// SaveMeeting saves a meeting func (p *Plugin) SaveMeeting(meeting *Meeting) error { jsonMeeting, err := json.Marshal(meeting) @@ -43,23 +46,24 @@ func (p *Plugin) SaveMeeting(meeting *Meeting) error { return err } - if err := p.API.KVSet(meeting.ChannelId, jsonMeeting); err != nil { - return err + if appErr := p.API.KVSet(meeting.ChannelID, jsonMeeting); appErr != nil { + return appErr } return nil } -func (p *Plugin) GenerateHashtag(channelId string, nextWeek bool) (string, error) { +// GenerateHashtag returns a meeting hashtag +func (p *Plugin) GenerateHashtag(channelID string, nextWeek bool) (string, error) { - meeting, err := p.GetMeeting(channelId) + meeting, err := p.GetMeeting(channelID) if err != nil { return "", err } meetingDate := nextWeekdayDate(meeting.Schedule, nextWeek) - hastag := fmt.Sprintf("#%v", meetingDate.Format(meeting.HashtagFormat)) + hashtag := fmt.Sprintf("#%v", meetingDate.Format(meeting.HashtagFormat)) - return hastag, nil + return hashtag, nil } diff --git a/server/plugin.go b/server/plugin.go index 190eedf..ced0e5c 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -40,7 +40,7 @@ func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Req } } -// OnActivate +// OnActivate is invoked when the plugin is activated func (p *Plugin) OnActivate() error { if err := p.registerCommands(); err != nil { return errors.Wrap(err, "failed to register commands") @@ -61,22 +61,22 @@ func (p *Plugin) OnActivate() error { func (p *Plugin) httpMeetingSettings(w http.ResponseWriter, r *http.Request) { - mattermostUserId := r.Header.Get("Mattermost-User-Id") - if mattermostUserId == "" { + mattermostUserID := r.Header.Get("Mattermost-User-Id") + if mattermostUserID == "" { http.Error(w, "Not Authorized", http.StatusUnauthorized) } switch r.Method { case http.MethodPost: - p.httpMeetingSaveSettings(w, r, mattermostUserId) + p.httpMeetingSaveSettings(w, r, mattermostUserID) case http.MethodGet: - p.httpMeetingGetSettings(w, r, mattermostUserId) + p.httpMeetingGetSettings(w, r, mattermostUserID) default: http.Error(w, "Request: "+r.Method+" is not allowed.", http.StatusMethodNotAllowed) } } -func (p *Plugin) httpMeetingSaveSettings(w http.ResponseWriter, r *http.Request, mmUserId string) { +func (p *Plugin) httpMeetingSaveSettings(w http.ResponseWriter, r *http.Request, mmUserID string) { userID := r.Header.Get("Mattermost-User-ID") if userID == "" { @@ -104,21 +104,21 @@ func (p *Plugin) httpMeetingSaveSettings(w http.ResponseWriter, r *http.Request, w.Write([]byte("{\"status\": \"OK\"}")) } -func (p *Plugin) httpMeetingGetSettings(w http.ResponseWriter, r *http.Request, mmUserId string) { +func (p *Plugin) httpMeetingGetSettings(w http.ResponseWriter, r *http.Request, mmUserID string) { userID := r.Header.Get("Mattermost-User-ID") if userID == "" { http.Error(w, "Not authorized", http.StatusUnauthorized) return } - channelId, ok := r.URL.Query()["channelId"] + channelID, ok := r.URL.Query()["channelId"] - if !ok || len(channelId[0]) < 1 { + if !ok || len(channelID[0]) < 1 { http.Error(w, "Missing channelId parameter", http.StatusBadRequest) return } - meeting, err := p.GetMeeting(channelId[0]) + meeting, err := p.GetMeeting(channelID[0]) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return diff --git a/server/plugin_test.go b/server/plugin_test.go index 4e7ea4c..08493c9 100644 --- a/server/plugin_test.go +++ b/server/plugin_test.go @@ -2,14 +2,15 @@ package main import ( "encoding/json" - "github.com/mattermost/mattermost-server/plugin/plugintest" - "github.com/stretchr/testify/assert" "io/ioutil" "net/http" "net/http/httptest" "strings" "testing" "time" + + "github.com/mattermost/mattermost-server/plugin/plugintest" + "github.com/stretchr/testify/assert" ) func TestServeHTTP(t *testing.T) { @@ -22,7 +23,7 @@ func TestServeHTTP(t *testing.T) { t.Run("get default meeting settings", func(t *testing.T) { // Mock get default meeting defaultMeeting := &Meeting{ - ChannelId: "myChannelId", + ChannelID: "myChannelId", Schedule: time.Thursday, HashtagFormat: "Jan02", } @@ -49,7 +50,7 @@ func TestServeHTTP(t *testing.T) { t.Run("post meeting settings", func(t *testing.T) { // Mock set meeting meeting := &Meeting{ - ChannelId: "myChannelId", + ChannelID: "myChannelId", Schedule: time.Tuesday, HashtagFormat: "MyMeeting-Jan-02", } diff --git a/webapp/src/components/meeting_settings/index.js b/webapp/src/components/meeting_settings/index.js index e1d6ad5..9a151d9 100644 --- a/webapp/src/components/meeting_settings/index.js +++ b/webapp/src/components/meeting_settings/index.js @@ -1,15 +1,15 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import {getMettingSettingsModalState, getMeetingSettings} from 'selectors'; +import {getMeetingSettingsModalState, getMeetingSettings} from 'selectors'; import {closeMeetingSettingsModal, fetchMeetingSettings, saveMeetingSettings} from 'actions'; import MeetingSettingsModal from './meeting_settings'; function mapStateToProps(state) { return { - visible: getMettingSettingsModalState(state).visible, - channelId: getMettingSettingsModalState(state).channelId, + visible: getMeetingSettingsModalState(state).visible, + channelId: getMeetingSettingsModalState(state).channelId, meeting: getMeetingSettings(state).meeting, saveMeetingSettings, }; diff --git a/webapp/src/selectors.js b/webapp/src/selectors.js index 4ac0c8f..af2cef7 100644 --- a/webapp/src/selectors.js +++ b/webapp/src/selectors.js @@ -2,5 +2,5 @@ import {id as pluginId} from './manifest'; const getPluginState = (state) => state['plugins-' + pluginId] || {}; -export const getMettingSettingsModalState = (state) => getPluginState(state).meetingSettingsModal; +export const getMeetingSettingsModalState = (state) => getPluginState(state).meetingSettingsModal; export const getMeetingSettings = (state) => getPluginState(state).meetingSettings;