Skip to content

Commit

Permalink
[MM-586] Added API token for fetching issue details (#1102)
Browse files Browse the repository at this point in the history
* [MM-586] Added API token for fetching issue details
1. Added API token field in syatem console.
2. Added logic to fetch issue details with API token for comment and issue created events.

* Remove junit file

* Review fixes

* Review fixes

* Update .gitignore file

* Review fix

* [MM-866] Add handling for 404 and 403 status code in response

* [MM-866] Moved the logic to use API token after trying to use the token of connected user

* [MM-866] Fix lint error
  • Loading branch information
raghavaggarwal2308 authored Oct 18, 2024
1 parent 1ff05f7 commit 85725f3
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 1 deletion.
8 changes: 8 additions & 0 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@
"help_text": "Display subscription name in post when a subscription posts to a channel",
"placeholder": "",
"default": false
},
{
"key": "AdminAPIToken",
"display_name": "Admin API Token",
"type": "text",
"help_text": "Set this [API token](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/) to get notified for comment and issue created events even if the user triggering the event is not connected to Jira.\n **Note:** API token should be created using an admin Jira account. Otherwise, the notification will not be delivered for the project the user cannot access.",
"placeholder": "",
"default": ""
}
]
}
Expand Down
40 changes: 40 additions & 0 deletions server/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"regexp"
"strconv"
Expand Down Expand Up @@ -1094,3 +1095,42 @@ func (p *Plugin) GetIssueByKey(instanceID, mattermostUserID types.ID, issueKey s
}
return issue, nil
}

func (p *Plugin) GetIssueDataWithAPIToken(issueID, instanceID string) (*jira.Issue, error) {
client := &http.Client{}
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/rest/api/2/issue/%s", instanceID, issueID), nil)
if err != nil {
return nil, errors.Wrapf(err, "failed to create http request for fetching issue data. IssueID: %s", issueID)
}

req.Header.Set("Authorization", fmt.Sprintf("Basic %s", p.getConfig().AdminAPIToken))

resp, err := client.Do(req)
if err != nil {
return nil, errors.Wrapf(err, "failed to get issue data. IssueID: %s", issueID)
}

if resp == nil || resp.Body == nil {
return nil, errors.Wrapf(err, "missing data for issue. StatusCode: %d, IssueID: %s", resp.StatusCode, issueID)
}

defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrapf(err, "failed to read issue data. StatusCode: %d, IssueID: %s", resp.StatusCode, issueID)
}

if resp.StatusCode == http.StatusNotFound {
return nil, errors.Errorf("issue does not exist or user does not have permission to fetch the issue details. StatusCode: %d, IssueID: %s", resp.StatusCode, issueID)
} else if resp.StatusCode == http.StatusForbidden {
return nil, errors.Errorf("user does not have permission to fetch the issue details. StatusCode: %d, IssueID: %s", resp.StatusCode, issueID)
}

issue := &jira.Issue{}
if err = json.Unmarshal(body, issue); err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal issue data. IssueID: %s", issueID)
}

return issue, nil
}
7 changes: 6 additions & 1 deletion server/kv.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,13 @@ func (store store) LoadMattermostUserID(instanceID types.ID, jiraUserNameOrID st
err := store.get(keyWithInstanceID(instanceID, types.ID(jiraUserNameOrID)), &mattermostUserID)
if err != nil {
return "", errors.Wrapf(err,
"failed to load Mattermost user ID for Jira user/ID: "+jiraUserNameOrID)
"failed to load Mattermost user ID for Jira username/ID: %s", jiraUserNameOrID)
}

if mattermostUserID == "" {
return "", errors.Errorf("failed to load Mattermost user ID for Jira username/ID: %s", jiraUserNameOrID)
}

return mattermostUserID, nil
}

Expand Down
3 changes: 3 additions & 0 deletions server/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ type externalConfig struct {

// Display subscription name in notifications
DisplaySubscriptionNameInNotifications bool

// API token from Jira
AdminAPIToken string
}

const defaultMaxAttachmentSize = utils.ByteSize(10 * 1024 * 1024) // 10Mb
Expand Down
11 changes: 11 additions & 0 deletions server/webhook_jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ func (jwh *JiraWebhook) expandIssue(p *Plugin, instanceID types.ID) error {
if err != nil {
// User is not connected, so we try to fall back to JWT bot
if instance.JWTInstance == nil {
// Using API token to fetch the issue details as users were not getting notified for the events triggered by a non connected user i.e. oauth token is absent
if p.getConfig().AdminAPIToken != "" {
issue, apiTokenErr := p.GetIssueDataWithAPIToken(jwh.Issue.Key, instance.GetID().String())
if apiTokenErr != nil {
return apiTokenErr
}

jwh.Issue = *issue
return nil
}

return errors.Wrap(err, "Cannot create subscription posts for this comment as the Jira comment author is not connected to Mattermost.")
}

Expand Down

0 comments on commit 85725f3

Please sign in to comment.