diff --git a/server/command/command.go b/server/command/command.go index 352e6ab..d7bdce7 100644 --- a/server/command/command.go +++ b/server/command/command.go @@ -33,7 +33,6 @@ var ConfluenceCommandHandler = Handler{ handlers: map[string]HandlerFunc{ "list": listChannelSubscription, "unsubscribe": deleteSubscription, - "edit": editSubscription, "help": confluenceHelp, }, defaultHandler: executeConfluenceDefault, @@ -109,18 +108,6 @@ func listChannelSubscription(context *model.CommandArgs, args ...string) *model. return &model.CommandResponse{} } -func editSubscription(context *model.CommandArgs, args ...string) *model.CommandResponse { - if len(args) == 0 { - postCommandResponse(context, specifyAlias) - return &model.CommandResponse{} - } - alias := args[0] - if err := service.OpenSubscriptionEditModal(context.ChannelId, context.UserId, alias); err != nil { - postCommandResponse(context, err.Error()) - } - return &model.CommandResponse{} -} - func confluenceHelp(context *model.CommandArgs, args ...string) *model.CommandResponse { postCommandResponse(context, helpText) return &model.CommandResponse{} diff --git a/server/controller/edit_subscription.go b/server/controller/edit_subscription.go index 9564b6e..9850f15 100644 --- a/server/controller/edit_subscription.go +++ b/server/controller/edit_subscription.go @@ -9,14 +9,14 @@ import ( "github.com/Brightscout/mattermost-plugin-confluence/server/service" ) -var EditChannelSubscription = &Endpoint{ +var editChannelSubscription = &Endpoint{ RequiresAuth: true, Path: "/subscription", Method: http.MethodPut, - Execute: editChannelSubscription, + Execute: handleEditChannelSubscription, } -func editChannelSubscription(w http.ResponseWriter, r *http.Request) { +func handleEditChannelSubscription(w http.ResponseWriter, r *http.Request) { body := json.NewDecoder(r.Body) subscription := serializer.Subscription{} if err := body.Decode(&subscription); err != nil { diff --git a/server/controller/get_subscription.go b/server/controller/get_subscription.go new file mode 100644 index 0000000..07ef5ee --- /dev/null +++ b/server/controller/get_subscription.go @@ -0,0 +1,26 @@ +package controller + +import ( + "net/http" + + "github.com/Brightscout/mattermost-plugin-confluence/server/service" +) + +var getChannelSubscription = &Endpoint{ + RequiresAuth: true, + Path: "/subscription", + Method: http.MethodGet, + Execute: handleGetChannelSubscription, +} + +func handleGetChannelSubscription(w http.ResponseWriter, r *http.Request) { + channelID := r.FormValue("channelID") + alias := r.FormValue("alias") + subscription, errCode, err := service.GetChannelSubscription(channelID, alias) + if err != nil { + http.Error(w, err.Error(), errCode) + return + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(subscription.ToJSON())) +} diff --git a/server/controller/main.go b/server/controller/main.go index 4b8c69d..70f8974 100644 --- a/server/controller/main.go +++ b/server/controller/main.go @@ -25,9 +25,10 @@ type Endpoint struct { // Usage: getEndpointKey(GetMetadata): GetMetadata var Endpoints = map[string]*Endpoint{ getEndpointKey(confluenceCloudWebhook): confluenceCloudWebhook, - getEndpointKey(SaveChannelSubscription): SaveChannelSubscription, - getEndpointKey(EditChannelSubscription): EditChannelSubscription, + getEndpointKey(saveChannelSubscription): saveChannelSubscription, + getEndpointKey(editChannelSubscription): editChannelSubscription, getEndpointKey(confluenceServerWebhook): confluenceServerWebhook, + getEndpointKey(getChannelSubscription): getChannelSubscription, } // Uniquely identifies an endpoint using path and method diff --git a/server/controller/save_subscription.go b/server/controller/save_subscription.go index df2a779..e35f108 100644 --- a/server/controller/save_subscription.go +++ b/server/controller/save_subscription.go @@ -9,14 +9,14 @@ import ( "github.com/Brightscout/mattermost-plugin-confluence/server/service" ) -var SaveChannelSubscription = &Endpoint{ +var saveChannelSubscription = &Endpoint{ RequiresAuth: true, Path: "/subscription", Method: http.MethodPost, - Execute: saveChannelSubscription, + Execute: handleSaveChannelSubscription, } -func saveChannelSubscription(w http.ResponseWriter, r *http.Request) { +func handleSaveChannelSubscription(w http.ResponseWriter, r *http.Request) { body := json.NewDecoder(r.Body) subscription := serializer.Subscription{} if err := body.Decode(&subscription); err != nil { diff --git a/server/serializer/channel_config.go b/server/serializer/channel_subscription.go similarity index 93% rename from server/serializer/channel_config.go rename to server/serializer/channel_subscription.go index 9d9d984..f37dbc4 100644 --- a/server/serializer/channel_config.go +++ b/server/serializer/channel_subscription.go @@ -1,6 +1,7 @@ package serializer import ( + "encoding/json" "fmt" url2 "net/url" "strings" @@ -66,3 +67,11 @@ func FormattedSubscriptionList(channelSubscriptions map[string]Subscription) str } return list } + +func (s *Subscription) ToJSON() string { + b, err := json.Marshal(s) + if err != nil { + return "" + } + return string(b) +} diff --git a/server/service/edit_subscription.go b/server/service/edit_subscription.go index 78b7735..b7cbb69 100644 --- a/server/service/edit_subscription.go +++ b/server/service/edit_subscription.go @@ -1,8 +1,6 @@ package service import ( - "encoding/json" - "fmt" "net/http" "github.com/mattermost/mattermost-server/model" @@ -13,11 +11,7 @@ import ( "github.com/Brightscout/mattermost-plugin-confluence/server/store" ) -const ( - openEditSubscriptionModalWebsocketEvent = "open_edit_subscription_modal" - generalError = "Some error occurred. Please try again after sometime." - subscriptionEditSuccess = "Subscription updated successfully." -) +const subscriptionEditSuccess = "Subscription updated successfully." func EditSubscription(subscription serializer.Subscription, userID string) (int, error) { channelSubscriptions, cKey, gErr := GetChannelSubscriptions(subscription.ChannelID) @@ -46,28 +40,3 @@ func EditSubscription(subscription serializer.Subscription, userID string) (int, return http.StatusOK, nil } - -func OpenSubscriptionEditModal(channelID, userID, alias string) error { - channelSubscriptions, _, gErr := GetChannelSubscriptions(channelID) - if gErr != nil { - return errors.New(generalError) - } - if subscription, ok := channelSubscriptions[alias]; ok { - bytes, err := json.Marshal(subscription) - if err != nil { - return errors.New(generalError) - } - config.Mattermost.PublishWebSocketEvent( - openEditSubscriptionModalWebsocketEvent, - map[string]interface{}{ - "subscription": string(bytes), - }, - &model.WebsocketBroadcast{ - UserId: userID, - }, - ) - return nil - } - - return errors.New(fmt.Sprintf(subscriptionNotFound, alias)) -} diff --git a/server/service/get_subscription.go b/server/service/get_subscription.go new file mode 100644 index 0000000..6e3e801 --- /dev/null +++ b/server/service/get_subscription.go @@ -0,0 +1,24 @@ +package service + +import ( + "fmt" + "net/http" + + "github.com/pkg/errors" + + "github.com/Brightscout/mattermost-plugin-confluence/server/serializer" +) + +const generalError = "Some error occurred. Please try again after sometime." + +func GetChannelSubscription(channelID, alias string) (serializer.Subscription, int, error) { + channelSubscriptions, _, gErr := GetChannelSubscriptions(channelID) + if gErr != nil { + return serializer.Subscription{}, http.StatusInternalServerError, errors.New(generalError) + } + subscription, found := channelSubscriptions[alias] + if !found { + return serializer.Subscription{}, http.StatusBadRequest, errors.New(fmt.Sprintf(subscriptionNotFound, alias)) + } + return subscription, http.StatusOK, nil +} diff --git a/server/service/list_subscription.go b/server/service/get_subscription_list.go similarity index 100% rename from server/service/list_subscription.go rename to server/service/get_subscription_list.go diff --git a/webapp/src/actions/index.js b/webapp/src/actions/index.js index 5d3009b..cf61b5b 100644 --- a/webapp/src/actions/index.js +++ b/webapp/src/actions/index.js @@ -1,11 +1,11 @@ import { - closeSubscriptionModal, openSubscriptionModal, saveChannelSubscription, receivedSubscription, editChannelSubscription, + closeSubscriptionModal, openSubscriptionModal, saveChannelSubscription, editChannelSubscription, getChannelSubscription, } from './subscription_modal'; export { saveChannelSubscription, openSubscriptionModal, closeSubscriptionModal, - receivedSubscription, editChannelSubscription, + getChannelSubscription, }; diff --git a/webapp/src/actions/subscription_modal.js b/webapp/src/actions/subscription_modal.js index 28e52ce..2f35b49 100644 --- a/webapp/src/actions/subscription_modal.js +++ b/webapp/src/actions/subscription_modal.js @@ -1,3 +1,5 @@ +import {PostTypes} from 'mattermost-redux/action_types'; + import Client from '../client'; import Constants from '../constants'; @@ -51,10 +53,36 @@ export const closeSubscriptionModal = () => (dispatch) => { }); }; -export const receivedSubscription = (subscription) => (dispatch) => { - dispatch({ - type: Constants.ACTION_TYPES.RECEIVED_SUBSCRIPTION, - data: JSON.parse(subscription), - }); +export const getChannelSubscription = (channelID, alias, userID) => async (dispatch) => { + try { + const response = await Client.getChannelSubscription(channelID, alias); + dispatch({ + type: Constants.ACTION_TYPES.RECEIVED_SUBSCRIPTION, + data: response, + }); + } catch (e) { + dispatch(sendEphemeralPost(e.response.text, channelID, userID)); + } }; +export function sendEphemeralPost(message, channelID, userID) { + const timestamp = Date.now(); + const post = { + id: 'confluencePlugin' + timestamp, + user_id: userID, + channel_id: channelID, + message, + type: 'system_ephemeral', + create_at: timestamp, + update_at: timestamp, + root_id: '', + parent_id: '', + props: {}, + }; + + return { + type: PostTypes.RECEIVED_NEW_POST, + data: post, + channelID, + }; +} diff --git a/webapp/src/client/client.js b/webapp/src/client/client.js index 17bd40b..4973a99 100644 --- a/webapp/src/client/client.js +++ b/webapp/src/client/client.js @@ -25,6 +25,11 @@ export default class Client { return this.doPut(url, channelSubscription); }; + getChannelSubscription = (channelID, alias) => { + const url = `${this.pluginApiUrl}/subscription?channelID=${channelID}&alias=${alias}`; + return this.doGet(url); + }; + doGet = async (url, headers = {}) => { headers['X-Requested-With'] = 'XMLHttpRequest'; diff --git a/webapp/src/constants/index.js b/webapp/src/constants/index.js index fcf11a2..7801a2c 100644 --- a/webapp/src/constants/index.js +++ b/webapp/src/constants/index.js @@ -38,6 +38,7 @@ const CONFLUENCE_EVENTS = [ const MATTERMOST_CSRF_COOKIE = 'MMCSRF'; const OPEN_EDIT_SUBSCRIPTION_MODAL_WEBSOCKET_EVENT = `custom_${PLUGIN_NAME}_open_edit_subscription_modal`; +const SPECIFY_ALIAS = 'Please specify alias.'; export default { ACTION_TYPES, @@ -45,4 +46,5 @@ export default { MATTERMOST_CSRF_COOKIE, OPEN_EDIT_SUBSCRIPTION_MODAL_WEBSOCKET_EVENT, PLUGIN_NAME, + SPECIFY_ALIAS, }; diff --git a/webapp/src/hooks/index.js b/webapp/src/hooks/index.js index fbe43a4..b0c6ff2 100644 --- a/webapp/src/hooks/index.js +++ b/webapp/src/hooks/index.js @@ -1,4 +1,8 @@ -import {openSubscriptionModal} from '../actions'; +import {openSubscriptionModal, getChannelSubscription} from '../actions'; + +import {splitArgs} from '../utils'; +import {sendEphemeralPost} from '../actions/subscription_modal'; +import Constants from '../constants'; export default class Hooks { constructor(store) { @@ -10,10 +14,18 @@ export default class Hooks { if (message) { commandTrimmed = message.trim(); } - + const userID = this.store.getState().entities.users.currentUserId; if (commandTrimmed && commandTrimmed === '/confluence subscribe') { this.store.dispatch(openSubscriptionModal()); return Promise.resolve({}); + } else if (commandTrimmed && commandTrimmed.startsWith('/confluence edit')) { + const args = splitArgs(commandTrimmed); + if (args.length < 3) { // eslint-disable-line + this.store.dispatch(sendEphemeralPost(Constants.SPECIFY_ALIAS, contextArgs.channel_id, userID)); + } else { + this.store.dispatch(getChannelSubscription(contextArgs.channel_id, args[2], userID)); + } + return Promise.resolve({}); } return Promise.resolve({ message, diff --git a/webapp/src/index.js b/webapp/src/index.js index 5e5ea64..34815e8 100644 --- a/webapp/src/index.js +++ b/webapp/src/index.js @@ -2,7 +2,6 @@ import Constants from './constants'; import Hooks from './hooks'; import reducer from './reducers'; -import {receivedSubscription} from './actions'; import SubscriptionModal from './components/subscription_modal'; // @@ -13,12 +12,6 @@ class PluginClass { initialize(registry, store) { registry.registerReducer(reducer); registry.registerRootComponent(SubscriptionModal); - registry.registerWebSocketEventHandler( - Constants.OPEN_EDIT_SUBSCRIPTION_MODAL_WEBSOCKET_EVENT, - (payload) => { - store.dispatch(receivedSubscription(payload.data.subscription)); - }, - ); const hooks = new Hooks(store); registry.registerSlashCommandWillBePostedHook(hooks.slashCommandWillBePostedHook); } diff --git a/webapp/src/utils/index.js b/webapp/src/utils/index.js new file mode 100644 index 0000000..0cae592 --- /dev/null +++ b/webapp/src/utils/index.js @@ -0,0 +1,12 @@ +export const splitArgs = (command) => { + const myRegexp = /[^\s"]+|"([^"]*)"/gi; + const myArray = []; + let match; + do { + match = myRegexp.exec(command); + if (match != null) { + myArray.push(match[1] ? match[1] : match[0]); + } + } while (match != null); + return myArray; +};