From 19b753467d37209f2227567637e60138d05dd405 Mon Sep 17 00:00:00 2001 From: Poornima Date: Mon, 27 Feb 2017 00:18:20 +0530 Subject: Adding edit of incoming webhook (#5272) Adding edit of outgoing webhook Fixing spelling of error Fixing style Changing from PUT to POST for updates Fixing test failures due to merge --- webapp/client/client.jsx | 24 ++ .../components/abstract_incoming_webhook.jsx | 242 +++++++++++ .../components/abstract_outgoing_webhook.jsx | 460 +++++++++++++++++++++ .../components/add_incoming_webhook.jsx | 223 +--------- .../components/add_outgoing_webhook.jsx | 432 +------------------ .../components/edit_incoming_webhook.jsx | 78 ++++ .../components/edit_outgoing_webhook.jsx | 190 +++++++++ .../components/installed_incoming_webhook.jsx | 11 +- .../components/installed_incoming_webhooks.jsx | 1 + .../components/installed_outgoing_webhook.jsx | 11 +- .../components/installed_outgoing_webhooks.jsx | 1 + webapp/i18n/en.json | 4 + webapp/routes/route_integrations.jsx | 12 + webapp/stores/integration_store.jsx | 18 + webapp/tests/client_hooks.test.jsx | 48 ++- webapp/utils/async_client.jsx | 46 +++ webapp/utils/constants.jsx | 1 + 17 files changed, 1163 insertions(+), 639 deletions(-) create mode 100644 webapp/components/integrations/components/abstract_incoming_webhook.jsx create mode 100644 webapp/components/integrations/components/abstract_outgoing_webhook.jsx create mode 100644 webapp/components/integrations/components/edit_incoming_webhook.jsx create mode 100644 webapp/components/integrations/components/edit_outgoing_webhook.jsx (limited to 'webapp') diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx index 24eb7eabb..390c07d13 100644 --- a/webapp/client/client.jsx +++ b/webapp/client/client.jsx @@ -1975,6 +1975,18 @@ export default class Client { this.trackEvent('api', 'api_integrations_created', {team_id: this.getTeamId()}); } + updateIncomingHook(hook, success, error) { + request. + post(`${this.getHooksRoute()}/incoming/update`). + set(this.defaultHeaders). + type('application/json'). + accept('application/json'). + send(hook). + end(this.handleResponse.bind(this, 'updateIncomingHook', success, error)); + + this.trackEvent('api', 'api_integrations_updated', {team_id: this.getTeamId()}); + } + deleteIncomingHook(hookId, success, error) { request. post(`${this.getHooksRoute()}/incoming/delete`). @@ -2008,6 +2020,18 @@ export default class Client { this.trackEvent('api', 'api_integrations_created', {team_id: this.getTeamId()}); } + updateOutgoingHook(hook, success, error) { + request. + post(`${this.getHooksRoute()}/outgoing/update`). + set(this.defaultHeaders). + type('application/json'). + accept('application/json'). + send(hook). + end(this.handleResponse.bind(this, 'updateOutgoingHook', success, error)); + + this.trackEvent('api', 'api_integrations_updated', {team_id: this.getTeamId()}); + } + deleteOutgoingHook(hookId, success, error) { request. post(`${this.getHooksRoute()}/outgoing/delete`). diff --git a/webapp/components/integrations/components/abstract_incoming_webhook.jsx b/webapp/components/integrations/components/abstract_incoming_webhook.jsx new file mode 100644 index 000000000..04322d77e --- /dev/null +++ b/webapp/components/integrations/components/abstract_incoming_webhook.jsx @@ -0,0 +1,242 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; + +import BackstageHeader from 'components/backstage/components/backstage_header.jsx'; +import ChannelSelect from 'components/channel_select.jsx'; +import {FormattedMessage} from 'react-intl'; +import FormError from 'components/form_error.jsx'; +import SpinnerButton from 'components/spinner_button.jsx'; +import {Link} from 'react-router/es6'; + +export default class AbstractIncomingWebhook extends React.Component { + static get propTypes() { + return { + team: React.PropTypes.object + }; + } + + constructor(props) { + super(props); + + this.handleSubmit = this.handleSubmit.bind(this); + + this.updateDisplayName = this.updateDisplayName.bind(this); + this.updateDescription = this.updateDescription.bind(this); + this.updateChannelId = this.updateChannelId.bind(this); + + this.state = { + displayName: '', + description: '', + channelId: '', + saving: false, + serverError: '', + clientError: null + }; + + if (typeof this.performAction === 'undefined') { + throw new TypeError('Subclasses must override performAction'); + } + + if (typeof this.header === 'undefined') { + throw new TypeError('Subclasses must override header'); + } + + if (typeof this.footer === 'undefined') { + throw new TypeError('Subclasses must override footer'); + } + + this.performAction = this.performAction.bind(this); + this.header = this.header.bind(this); + this.footer = this.footer.bind(this); + } + + handleSubmit(e) { + e.preventDefault(); + + if (this.state.saving) { + return; + } + + this.setState({ + saving: true, + serverError: '', + clientError: '' + }); + + if (!this.state.channelId) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + + return; + } + + const hook = { + channel_id: this.state.channelId, + display_name: this.state.displayName, + description: this.state.description + }; + + this.performAction(hook); + } + + updateDisplayName(e) { + this.setState({ + displayName: e.target.value + }); + } + + updateDescription(e) { + this.setState({ + description: e.target.value + }); + } + + updateChannelId(e) { + this.setState({ + channelId: e.target.value + }); + } + + render() { + var headerToRender = this.header(); + var footerToRender = this.footer(); + return ( +
+ + + + + + +
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ + + + + + + +
+
+
+
+ ); + } +} diff --git a/webapp/components/integrations/components/abstract_outgoing_webhook.jsx b/webapp/components/integrations/components/abstract_outgoing_webhook.jsx new file mode 100644 index 000000000..6033647af --- /dev/null +++ b/webapp/components/integrations/components/abstract_outgoing_webhook.jsx @@ -0,0 +1,460 @@ +// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; + +import {localizeMessage} from 'utils/utils.jsx'; + +import BackstageHeader from 'components/backstage/components/backstage_header.jsx'; +import ChannelSelect from 'components/channel_select.jsx'; +import {FormattedMessage} from 'react-intl'; +import FormError from 'components/form_error.jsx'; +import {Link} from 'react-router/es6'; +import SpinnerButton from 'components/spinner_button.jsx'; + +export default class AbstractOutgoingWebhook extends React.Component { + static get propTypes() { + return { + team: React.PropTypes.object + }; + } + + constructor(props) { + super(props); + + this.handleSubmit = this.handleSubmit.bind(this); + + this.updateDisplayName = this.updateDisplayName.bind(this); + this.updateDescription = this.updateDescription.bind(this); + this.updateContentType = this.updateContentType.bind(this); + this.updateChannelId = this.updateChannelId.bind(this); + this.updateTriggerWords = this.updateTriggerWords.bind(this); + this.updateTriggerWhen = this.updateTriggerWhen.bind(this); + this.updateCallbackUrls = this.updateCallbackUrls.bind(this); + + this.state = { + displayName: '', + description: '', + contentType: 'application/x-www-form-urlencoded', + channelId: '', + triggerWords: '', + triggerWhen: 0, + callbackUrls: '', + saving: false, + serverError: '', + clientError: null + }; + + if (typeof this.performAction === 'undefined') { + throw new TypeError('Subclasses must override performAction'); + } + + if (typeof this.header === 'undefined') { + throw new TypeError('Subclasses must override header'); + } + + if (typeof this.footer === 'undefined') { + throw new TypeError('Subclasses must override footer'); + } + + if (typeof this.renderExtra === 'undefined') { + throw new TypeError('Subclasses must override renderExtra'); + } + + this.performAction = this.performAction.bind(this); + this.header = this.header.bind(this); + this.footer = this.footer.bind(this); + this.renderExtra = this.renderExtra.bind(this); + } + + handleSubmit(e) { + e.preventDefault(); + + if (this.state.saving) { + return; + } + + this.setState({ + saving: true, + serverError: '', + clientError: '' + }); + + const triggerWords = []; + if (this.state.triggerWords) { + for (let triggerWord of this.state.triggerWords.split('\n')) { + triggerWord = triggerWord.trim(); + + if (triggerWord.length > 0) { + triggerWords.push(triggerWord); + } + } + } + + if (!this.state.channelId && triggerWords.length === 0) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + + return; + } + + const callbackUrls = []; + for (let callbackUrl of this.state.callbackUrls.split('\n')) { + callbackUrl = callbackUrl.trim(); + + if (callbackUrl.length > 0) { + callbackUrls.push(callbackUrl); + } + } + + if (callbackUrls.length === 0) { + this.setState({ + saving: false, + clientError: ( + + ) + }); + + return; + } + + const hook = { + channel_id: this.state.channelId, + trigger_words: triggerWords, + trigger_when: parseInt(this.state.triggerWhen, 10), + callback_urls: callbackUrls, + display_name: this.state.displayName, + content_type: this.state.contentType, + description: this.state.description + }; + + this.performAction(hook); + } + + updateDisplayName(e) { + this.setState({ + displayName: e.target.value + }); + } + + updateDescription(e) { + this.setState({ + description: e.target.value + }); + } + + updateContentType(e) { + this.setState({ + contentType: e.target.value + }); + } + + updateChannelId(e) { + this.setState({ + channelId: e.target.value + }); + } + + updateTriggerWords(e) { + this.setState({ + triggerWords: e.target.value + }); + } + + updateTriggerWhen(e) { + this.setState({ + triggerWhen: e.target.value + }); + } + + updateCallbackUrls(e) { + this.setState({ + callbackUrls: e.target.value + }); + } + + render() { + const contentTypeOption1 = 'application/x-www-form-urlencoded'; + const contentTypeOption2 = 'application/json'; + + var headerToRender = this.header(); + var footerToRender = this.footer(); + var renderExtra = this.renderExtra(); + + return ( +
+ + + + + + +
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+