diff --git a/server/instance_cloud_oauth.go b/server/instance_cloud_oauth.go index 78489bd7a..fe106520a 100644 --- a/server/instance_cloud_oauth.go +++ b/server/instance_cloud_oauth.go @@ -218,11 +218,11 @@ func (ci *cloudOAuthInstance) GetJiraBaseURL() string { } func (ci *cloudOAuthInstance) GetManageAppsURL() string { - return fmt.Sprintf("%s/plugins/servlet/applinks/listApplicationLinks", ci.GetURL()) + return fmt.Sprintf("%s/plugins/servlet/applinks/listApplicationLinks", ci.GetJiraBaseURL()) } func (ci *cloudOAuthInstance) GetManageWebhooksURL() string { - return fmt.Sprintf("%s/plugins/servlet/webhooks", ci.GetURL()) + return fmt.Sprintf("%s/plugins/servlet/webhooks", ci.GetJiraBaseURL()) } func (ci *cloudOAuthInstance) GetMattermostKey() string { diff --git a/webapp/src/action_types/index.js b/webapp/src/action_types/index.js index c8ad0aca5..75e0806ee 100644 --- a/webapp/src/action_types/index.js +++ b/webapp/src/action_types/index.js @@ -32,6 +32,4 @@ export default { RECEIVED_CHANNEL_SUBSCRIPTIONS: `${PluginId}_recevied_channel_subscriptions`, DELETED_CHANNEL_SUBSCRIPTION: `${PluginId}_deleted_channel_subscription`, - - RECEIVED_JIRA_TICKET: `${PluginId}_received_jira_ticket`, }; diff --git a/webapp/src/actions/index.ts b/webapp/src/actions/index.ts index 3022be061..fc4bdce53 100644 --- a/webapp/src/actions/index.ts +++ b/webapp/src/actions/index.ts @@ -526,11 +526,6 @@ export const fetchIssueByKey = (issueKey: string, instanceID: string) => { data = await doFetch(`${baseUrl}/api/v2/get-issue-by-key?${params}`, { method: 'get', }); - - dispatch({ - type: ActionTypes.RECEIVED_JIRA_TICKET, - data, - }); return {data}; } catch (error) { return {error}; diff --git a/webapp/src/components/jira_ticket_tooltip/index.ts b/webapp/src/components/jira_ticket_tooltip/index.ts index 5df7ab161..32e538acd 100644 --- a/webapp/src/components/jira_ticket_tooltip/index.ts +++ b/webapp/src/components/jira_ticket_tooltip/index.ts @@ -2,9 +2,7 @@ import {connect} from 'react-redux'; import {bindActionCreators, Dispatch} from 'redux'; import {GlobalState} from 'mattermost-redux/types/store'; -import {jiraIssueToReducer} from 'utils/jira_issue_metadata'; - -import {isUserConnected, getStoredLinkTooltipIssue, getUserConnectedInstances, getDefaultUserInstanceID} from 'selectors'; +import {isUserConnected, getUserConnectedInstances} from 'selectors'; import {fetchIssueByKey} from 'actions'; import TicketPopover from './jira_ticket_tooltip'; @@ -12,7 +10,6 @@ import TicketPopover from './jira_ticket_tooltip'; const mapStateToProps = (state: GlobalState) => { return { connected: isUserConnected(state), - ticketDetails: jiraIssueToReducer(getStoredLinkTooltipIssue(state).ticket), connectedInstances: getUserConnectedInstances(state), }; }; diff --git a/webapp/src/components/jira_ticket_tooltip/jira_ticket_tooltip.tsx b/webapp/src/components/jira_ticket_tooltip/jira_ticket_tooltip.tsx index 872a999ec..58b5ff048 100644 --- a/webapp/src/components/jira_ticket_tooltip/jira_ticket_tooltip.tsx +++ b/webapp/src/components/jira_ticket_tooltip/jira_ticket_tooltip.tsx @@ -1,19 +1,18 @@ import React, {ReactNode} from 'react'; -import {Dispatch} from 'redux'; import {Instance} from 'types/model'; import {TicketData, TicketDetails} from 'types/tooltip'; import DefaultAvatar from 'components/default_avatar/default_avatar'; import './ticketStyle.scss'; +import {getJiraTicketDetails} from 'utils/jira_issue_metadata'; export type Props = { href: string; show: boolean; connected: boolean; - ticketDetails?: TicketDetails | null; connectedInstances: Instance[]; - fetchIssueByKey: (issueKey: string, instanceID: string) => Promise; + fetchIssueByKey: (issueKey: string, instanceID: string) => Promise<{data?: TicketData}>; } export type State = { @@ -78,25 +77,6 @@ export default class TicketPopover extends React.PureComponent { return null; } - isUserConnectedAndStateNotLoaded() { - const {connected} = this.props; - const {ticketDetails} = this.state; - - return Boolean(connected && ticketDetails); - } - - componentDidMount() { - if (!this.state.ticketId) { - return; - } - - const {ticketDetails} = this.props; - const {ticketId} = this.state; - if (this.isUserConnectedAndStateNotLoaded() && ticketDetails && ticketDetails.ticketId === ticketId) { - this.setTicket(this.props); - } - } - componentDidUpdate() { const issueKey = this.getIssueKey(); if (!issueKey) { @@ -104,22 +84,19 @@ export default class TicketPopover extends React.PureComponent { } const {instanceID} = issueKey; - const {ticketDetails} = this.props; - const {ticketId, ticketDetails: localTicketDetails} = this.state; - - if (!localTicketDetails && ticketDetails && ticketDetails.ticketId === ticketId) { - this.setTicket(this.props); - } else if (!localTicketDetails && this.props.show && ticketId) { - this.props.fetchIssueByKey(ticketId, instanceID); + const {ticketId, ticketDetails} = this.state; + if (!ticketDetails && this.props.show && ticketId) { + this.props.fetchIssueByKey(this.state.ticketId, instanceID).then((res: {data?: TicketData}) => { + const updatedTicketDetails = getJiraTicketDetails(res.data); + if (this.props.connected && updatedTicketDetails && updatedTicketDetails.ticketId === ticketId) { + this.setState({ + ticketDetails: updatedTicketDetails, + }); + } + }); } } - setTicket(data: Props) { - this.setState({ - ticketDetails: data.ticketDetails, - }); - } - fixVersionLabel(fixVersion: string) { if (fixVersion) { const fixVersionString = 'Fix Version :'; @@ -139,7 +116,7 @@ export default class TicketPopover extends React.PureComponent { tagTicketStatus(ticketStatus: string) { let ticketStatusClass = 'default-style ticket-status--default'; - const myStatusClass = myStatusClasses[ticketStatus.toLowerCase()]; + const myStatusClass = myStatusClasses[ticketStatus && ticketStatus.toLowerCase()]; if (myStatusClass) { ticketStatusClass = 'default-style ' + myStatusClass; } @@ -148,7 +125,7 @@ export default class TicketPopover extends React.PureComponent { } renderLabelList(labels: string[]) { - if (!labels.length) { + if (!labels || !labels.length) { return null; } @@ -243,7 +220,7 @@ export default class TicketPopover extends React.PureComponent { target='_blank' rel='noopener noreferrer' > -
{ticketDetails.summary.substring(0, jiraTicketSummaryMaxLength)}
+
{ticketDetails.summary && ticketDetails.summary.substring(0, jiraTicketSummaryMaxLength)}
{this.tagTicketStatus(ticketDetails.statusKey)} diff --git a/webapp/src/utils/jira_issue_metadata.test.tsx b/webapp/src/utils/jira_issue_metadata.test.tsx index 7349a5020..f3c401b53 100644 --- a/webapp/src/utils/jira_issue_metadata.test.tsx +++ b/webapp/src/utils/jira_issue_metadata.test.tsx @@ -8,7 +8,7 @@ import {useFieldForIssueMetadata} from 'testdata/jira-issue-metadata-helpers'; import {IssueMetadata, JiraField, FilterField, ChannelSubscriptionFilters, FilterFieldInclusion, IssueType, Project} from 'types/model'; import {IssueAction, TicketDetails} from 'types/tooltip'; -import {getCustomFieldFiltersForProjects, generateJQLStringFromSubscriptionFilters, getConflictingFields, jiraIssueToReducer} from './jira_issue_metadata'; +import {getCustomFieldFiltersForProjects, generateJQLStringFromSubscriptionFilters, getConflictingFields, getJiraTicketDetails} from './jira_issue_metadata'; describe('utils/jira_issue_metadata', () => { const useField = (field: JiraField, key: string): IssueMetadata => { @@ -593,7 +593,7 @@ describe('utils/jira_issue_metadata', () => { }); }); - describe('jiraIssueToReducer', () => { + describe('getJiraTicketDetails', () => { it('should return the ticket details with all fields', () => { const action: IssueAction = ticketData('Mock Name'); @@ -610,7 +610,7 @@ describe('utils/jira_issue_metadata', () => { issueIcon: 'https://something.atlassian.net/issuetype.png', }; - const result = jiraIssueToReducer(action.data); + const result = getJiraTicketDetails(action.data); expect(result).toEqual(expectedTicketDetails); }); @@ -631,7 +631,7 @@ describe('utils/jira_issue_metadata', () => { issueIcon: 'https://something.atlassian.net/issuetype.png', }; - const result = jiraIssueToReducer(action.data); + const result = getJiraTicketDetails(action.data); expect(result).toEqual(expectedTicketDetails); }); diff --git a/webapp/src/utils/jira_issue_metadata.tsx b/webapp/src/utils/jira_issue_metadata.tsx index 4917466ac..26f1140aa 100644 --- a/webapp/src/utils/jira_issue_metadata.tsx +++ b/webapp/src/utils/jira_issue_metadata.tsx @@ -376,23 +376,25 @@ export function generateJQLStringFromSubscriptionFilters(issueMetadata: IssueMet return [projectJQL, issueTypesJQL, filterFieldsJQL].filter(Boolean).join(' AND '); } -export function jiraIssueToReducer(data: TicketData): TicketDetails | null { +export function getJiraTicketDetails(data?: TicketData): TicketDetails | null { if (!data) { return null; } const assignee = data && data.fields && data.fields.assignee ? data.fields.assignee : null; const ticketDetails: TicketDetails = { + + // TODO: Add optional chaining operator assigneeName: (assignee && assignee.displayName) || '', assigneeAvatar: (assignee && assignee.avatarUrls && assignee.avatarUrls['48x48']) || '', - labels: data.fields.labels, - description: data.fields.description, - summary: data.fields.summary, + labels: data.fields && data.fields.labels, + description: data.fields && data.fields.description, + summary: data.fields && data.fields.summary, ticketId: data.key, - jiraIcon: data.fields.project.avatarUrls && data.fields.project.avatarUrls['48x48'], - versions: data.fields.versions.length ? data.fields.versions[0] : '', - statusKey: data.fields.status.name, - issueIcon: data.fields.issuetype.iconUrl, + jiraIcon: data.fields && data.fields.project && data.fields.project.avatarUrls && data.fields.project.avatarUrls['48x48'], + versions: data.fields && data.fields.versions && data.fields.versions.length ? data.fields.versions[0] : '', + statusKey: data.fields && data.fields.status && data.fields.status.name, + issueIcon: data.fields && data.fields.issuetype && data.fields.issuetype.iconUrl, }; return ticketDetails; }