Skip to content

Commit 23d6802

Browse files
itssapirtalihaff
andauthored
Jira improve error messaging (demisto#41580)
* Add configuration error handling for bad cloud configurations * bump version * Update message strings and description * Pre-commit fixes * Improve url check * update error messages * add UT * Apply suggestions from doc review Co-authored-by: talihaff <[email protected]> * Improve doc string for test_module --------- Co-authored-by: talihaff <[email protected]>
1 parent 481b5b0 commit 23d6802

File tree

6 files changed

+123
-16
lines changed

6 files changed

+123
-16
lines changed

Packs/Jira/.secrets-ignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ ATLASSIAN_AUTH_URL
1414
MagicMock
1515
epic_key
1616
https://yourcompany.atlassian.net
17-
https://dummy_url
17+
https://dummy_url
18+
https://test.atlassian.net.evil.com

Packs/Jira/Integrations/JiraV3/JiraV3.py

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from collections.abc import Callable
55
from copy import deepcopy
66
from mimetypes import guess_type
7+
from urllib.parse import urlparse
78

89

910
import demistomock as demisto # noqa: F401
@@ -3544,11 +3545,24 @@ def jira_test_authorization(client: JiraBaseClient, args: Dict[str, Any]) -> Com
35443545
return CommandResults(readable_output="Successful connection.")
35453546

35463547

3547-
def jira_test_module(client: JiraBaseClient) -> str:
3548-
"""This method will return an error since in order for the user to test the connectivity of the instance,
3549-
they have to run a separate command, therefore, pressing the `test` button on the configuration screen will
3550-
show them the steps in order to test the instance.
3548+
def jira_test_module(client: JiraBaseClient, params: Dict[str, Any]) -> str:
35513549
"""
3550+
Tests for basic configuration issues in the instance.
3551+
Tests the connectivity for basic authentication methods, otherwise provides users with further authentication instructions.
3552+
"""
3553+
url = params.get("server_url", "").rstrip("/")
3554+
cloudid = params.get("cloud_id")
3555+
3556+
if is_jira_cloud_url(url) and not cloudid:
3557+
raise DemistoException(
3558+
"Cloud ID is required for Jira Cloud instances. Refer to the integration help section for more information."
3559+
)
3560+
if cloudid and url != "https://api.atlassian.com/ex/jira":
3561+
raise DemistoException(
3562+
"Jira Cloud instances must use the default Server URL: `https://api.atlassian.com/ex/jira`."
3563+
" Please update the Server URL in the instance configuration."
3564+
)
3565+
35523566
if client.is_basic_auth or client.is_pat_auth:
35533567
client.jira_test_instance_connection() # raises on failure
35543568
return "ok"
@@ -4745,6 +4759,59 @@ def validate_auth_params(username: str, api_key: str, client_id: str, client_sec
47454759
raise DemistoException("To use OAuth 2.0, the 'Client ID' and 'Client Secret' parameters are mandatory.")
47464760

47474761

4762+
def is_jira_cloud_url(url: str) -> bool:
4763+
"""
4764+
Check if the given URL is a Jira Cloud Server URL.
4765+
4766+
Args:
4767+
url (str): The URL to parse.
4768+
4769+
Returns:
4770+
bool: True if the URL is a Jira Cloud URL, False otherwise.
4771+
"""
4772+
try:
4773+
hostname = urlparse(url).hostname or ""
4774+
return hostname.endswith((".atlassian.net", ".atlassian.com"))
4775+
4776+
except (ValueError, AttributeError):
4777+
return False
4778+
4779+
4780+
def add_config_error_messages(err: str, cloud_id: str, server_url: str) -> str:
4781+
"""
4782+
Provide additional information for error messages that result from incorrect configurations.
4783+
4784+
Args:
4785+
err (str): The original error message.
4786+
cloud_id (str): The cloud ID.
4787+
server_url (str): The server URL.
4788+
4789+
Returns:
4790+
str: The error message with additional information if applicable.
4791+
"""
4792+
4793+
if "404" in err and cloud_id and server_url.rstrip("/") != "https://api.atlassian.com/ex/jira":
4794+
err = f"""
4795+
(Error 404) Jira Cloud instances must use the default Server URL: `https://api.atlassian.com/ex/jira`.
4796+
Update the Server URL in the instance configuration and try again.
4797+
4798+
4799+
Original error: {err}
4800+
"""
4801+
4802+
elif "410" in err and not cloud_id and is_jira_cloud_url(server_url):
4803+
err = f"""
4804+
(Error 410) The requested endpoint has been removed from Jira On-Prem.
4805+
This appears to be a Jira Cloud instance. Please update the Cloud ID in the instance configuration and try again.
4806+
Refer to the integration help section for more information.
4807+
4808+
4809+
Original error: {err}
4810+
"""
4811+
4812+
return err
4813+
4814+
47484815
def main(): # pragma: no cover
47494816
params: Dict[str, Any] = demisto.params()
47504817
args = map_v2_args_to_v3(demisto.args())
@@ -4865,7 +4932,7 @@ def main(): # pragma: no cover
48654932
demisto.debug(f"The configured Jira client is: {type(client)}")
48664933

48674934
if command == "test-module":
4868-
return_results(jira_test_module(client=client))
4935+
return_results(jira_test_module(client=client, params=params))
48694936
elif command in commands:
48704937
return_results(commands[command](client, args))
48714938
elif command == "fetch-incidents":
@@ -4920,7 +4987,9 @@ def main(): # pragma: no cover
49204987
raise NotImplementedError(f"{command} command is not implemented.")
49214988

49224989
except Exception as e:
4923-
return_error(str(e))
4990+
err = add_config_error_messages(str(e), cloud_id, server_url)
4991+
4992+
return_error(err)
49244993

49254994

49264995
if __name__ in ["__main__", "builtin", "builtins"]:

Packs/Jira/Integrations/JiraV3/JiraV3_description.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
Please configure only one of the following fields:
1+
Configure your Jira instance based on the type of your deployment:
22

3-
1. Cloud ID - Used for Jira Cloud instance.
4-
2. OnPrem - Leave the Cloud ID empty, and fill in the rest of the fields.
3+
| Deployment Type | Cloud ID | Server URL |
4+
|-----------------|----------|------------|
5+
| Jira Cloud | Cloud ID of your Jira instance (See below) | `https://api.atlassian.com/ex/jira` |
6+
| Jira OnPrem | Leave empty | Your Jira server URL |
57

68
##### Cloud ID
79

@@ -10,16 +12,19 @@ Please configure only one of the following fields:
1012
`https://admin.atlassian.com/s/{cloud_id}/users`
1113

1214
#### Authentication Methods
15+
1316
There are 2 authentication methods:
1417
- **Basic Authentication**
1518
- **OAuth 2.0**
1619

1720
### Basic Authentication
21+
1822
Leave the *Client ID* and *Client Secret* fields empty and fill in the following fields:
1923
- *User name* - Enter your user email.
20-
- *API key* - Enter the API token. To generate API token, see [here](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/)
24+
- *API key* - Enter the API token. To generate an API token, see [here](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/)
2125

2226
##### Basic Authentication permissions
27+
2328
Ensure that you possess the necessary permissions by navigating to **Project Settings** > **Permissions** in the project.
2429
Locate permissions for the tasks listed below:
2530
* Browse projects
@@ -31,7 +36,8 @@ Locate permissions for the tasks listed below:
3136
* Add comments
3237
* Link issues
3338
### OAuth 2.0
34-
For both instances (Cloud ID & OnPrem), it is advised to use the `https://oproxy.demisto.ninja/authcode` **Callback URL**. The OProxy URL is a client side only web page that provides an easy interface to copy the obtained auth code from the authorization response to the integration configuration in the authorization flow steps. Optionally, if you don't want to use the OProxy URL, you can use a localhost URL on a port which is not used locally on your machine. For example: <http://localhost:9004>. You will then need to copy the code from the URL address bar in the response.
39+
40+
For both instances (Cloud ID & OnPrem), use the `https://oproxy.demisto.ninja/authcode` **Callback URL**. The OProxy URL is a client side web page that provides an easy interface to copy the authorization code from the response to the integration configuration during the authorization flow. Optionally, if you don't want to use the OProxy URL, you can use a localhost URL on an unused local port, for example: <http://localhost:9004>. You will then need to copy the code from the URL address bar in the response.
3541

3642
#### Cloud authentication
3743

Packs/Jira/Integrations/JiraV3/JiraV3_test.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ def test_test_module_basic_auth(mocker):
303303

304304
client = jira_base_client_mock("dummy_username", "dummy_api_key")
305305
mocker.patch.object(client, "jira_test_instance_connection")
306-
assert jira_test_module(client) == "ok"
306+
assert jira_test_module(client, params={}) == "ok"
307307

308308

309309
def test_test_module_pat(mocker):
@@ -319,7 +319,7 @@ def test_test_module_pat(mocker):
319319

320320
client = jira_base_client_mock(pat="dummy_pat")
321321
mocker.patch.object(client, "jira_test_instance_connection")
322-
assert jira_test_module(client) == "ok"
322+
assert jira_test_module(client, params={}) == "ok"
323323

324324

325325
def test_module_oauth2(mocker):
@@ -336,7 +336,7 @@ def test_module_oauth2(mocker):
336336
client = jira_base_client_mock()
337337
mocker.patch.object(client, "jira_test_instance_connection")
338338
with pytest.raises(DemistoException, match="In order to authorize the instance, first run the command `!jira-oauth-start`."):
339-
jira_test_module(client)
339+
jira_test_module(client, params={})
340340

341341

342342
@pytest.mark.parametrize(
@@ -3579,3 +3579,28 @@ def test_get_remote_data_preview_command():
35793579

35803580
# Validate interactions
35813581
mock_client.get_issue.assert_called_once_with(issue_id_or_key="JIRA-123")
3582+
3583+
3584+
@pytest.mark.parametrize(
3585+
"url, expected_is_cloud",
3586+
[
3587+
("https://yourcompany.atlassian.net", True),
3588+
("https://api.atlassian.com/ex/jira/", True),
3589+
("https://www.callback.com", False),
3590+
("https://test.atlassian.net.evil.com", False),
3591+
("https://dummy_url", False),
3592+
],
3593+
)
3594+
def test_is_jira_cloud_url(url, expected_is_cloud):
3595+
"""
3596+
Given:
3597+
- Various URL strings including Jira Cloud URLs, on-premises URLs, and invalid URLs
3598+
When:
3599+
- Calling is_jira_cloud_url function
3600+
Then:
3601+
- Validate that True is returned for Jira Cloud URLs and False for others
3602+
"""
3603+
from JiraV3 import is_jira_cloud_url
3604+
3605+
result = is_jira_cloud_url(url)
3606+
assert result == expected_is_cloud

Packs/Jira/ReleaseNotes/3_3_10.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
#### Integrations
3+
4+
##### Atlassian Jira v3
5+
6+
- Improved error messages for some misconfigured instances.

Packs/Jira/pack_metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "Atlassian Jira",
33
"description": "Use the Jira integration to manage issues and create Cortex XSOAR incidents from Jira projects.",
44
"support": "xsoar",
5-
"currentVersion": "3.3.9",
5+
"currentVersion": "3.3.10",
66
"author": "Cortex XSOAR",
77
"url": "https://www.paloaltonetworks.com/cortex",
88
"email": "",

0 commit comments

Comments
 (0)