t-bug-catcher 0.2.1__tar.gz → 0.2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/PKG-INFO +1 -1
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/setup.cfg +1 -1
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/setup.py +1 -1
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/__init__.py +1 -1
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/bug_catcher.py +1 -1
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/jira.py +71 -41
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher.egg-info/PKG-INFO +1 -1
- t_bug_catcher-0.2.3/tests/test_t_bug_catcher.py +72 -0
- t_bug_catcher-0.2.1/tests/test_t_bug_catcher.py +0 -10
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/MANIFEST.in +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/README.rst +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/pyproject.toml +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/requirements.txt +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/bug_snag.py +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/config.py +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/exceptions.py +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/utils/__init__.py +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/utils/common.py +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/utils/logger.py +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher/workitems.py +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher.egg-info/SOURCES.txt +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher.egg-info/dependency_links.txt +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher.egg-info/not-zip-safe +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher.egg-info/requires.txt +0 -0
- {t_bug_catcher-0.2.1 → t_bug_catcher-0.2.3}/t_bug_catcher.egg-info/top_level.txt +0 -0
|
@@ -98,7 +98,7 @@ class BugCatcher:
|
|
|
98
98
|
def report_error(
|
|
99
99
|
self,
|
|
100
100
|
exception: Optional[Exception] = None,
|
|
101
|
-
description: Optional[str] =
|
|
101
|
+
description: Optional[str] = None,
|
|
102
102
|
metadata: Optional[dict] = None,
|
|
103
103
|
attachments: Optional[List] = None,
|
|
104
104
|
assignee: Optional[str] = None,
|
|
@@ -33,7 +33,6 @@ def retry_if_bad_request(func):
|
|
|
33
33
|
return func(*args, **kwargs)
|
|
34
34
|
except BadRequestError as ex:
|
|
35
35
|
nonlocal attempt
|
|
36
|
-
print(f"Bad request Attempt {attempt}...", "WARN")
|
|
37
36
|
attempt = attempt + 1 if attempt < tries else 1
|
|
38
37
|
raise ex
|
|
39
38
|
|
|
@@ -404,12 +403,8 @@ class Jira:
|
|
|
404
403
|
"text": "Run link: ",
|
|
405
404
|
"marks": [{"type": "strong"}],
|
|
406
405
|
},
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
"text": CONFIG.ENVIRONMENT,
|
|
410
|
-
"marks": ([] + self.__link_markup()),
|
|
411
|
-
},
|
|
412
|
-
],
|
|
406
|
+
]
|
|
407
|
+
+ self.__link_markup(),
|
|
413
408
|
}
|
|
414
409
|
]
|
|
415
410
|
if CONFIG.ENVIRONMENT != "local"
|
|
@@ -529,19 +524,39 @@ class Jira:
|
|
|
529
524
|
link_markup = {
|
|
530
525
|
variables.get("environment"): [
|
|
531
526
|
{
|
|
532
|
-
"type": "
|
|
533
|
-
"
|
|
534
|
-
|
|
535
|
-
|
|
527
|
+
"type": "text",
|
|
528
|
+
"text": variables.get("processRunUrl"),
|
|
529
|
+
"marks": [
|
|
530
|
+
{
|
|
531
|
+
"type": "link",
|
|
532
|
+
"attrs": {"href": variables.get("processRunUrl")},
|
|
533
|
+
},
|
|
534
|
+
{"type": "underline"},
|
|
535
|
+
],
|
|
536
|
+
}
|
|
536
537
|
],
|
|
537
538
|
"robocloud": [
|
|
538
539
|
{
|
|
539
|
-
"type": "
|
|
540
|
-
"
|
|
541
|
-
|
|
542
|
-
|
|
540
|
+
"type": "text",
|
|
541
|
+
"text": "https://cloud.robocorp.com",
|
|
542
|
+
"marks": [
|
|
543
|
+
{
|
|
544
|
+
"type": "link",
|
|
545
|
+
"attrs": {"href": CONFIG.RC_RUN_LINK},
|
|
546
|
+
},
|
|
547
|
+
{"type": "underline"},
|
|
548
|
+
],
|
|
549
|
+
}
|
|
550
|
+
],
|
|
551
|
+
"local": [
|
|
552
|
+
{
|
|
553
|
+
"type": "text",
|
|
554
|
+
"text": "local run",
|
|
555
|
+
"marks": [
|
|
556
|
+
{"type": "underline"},
|
|
557
|
+
],
|
|
558
|
+
}
|
|
543
559
|
],
|
|
544
|
-
"local": [{"type": "underline"}],
|
|
545
560
|
}
|
|
546
561
|
return link_markup[CONFIG.ENVIRONMENT]
|
|
547
562
|
|
|
@@ -653,28 +668,33 @@ class Jira:
|
|
|
653
668
|
if len(error) < CONFIG.LIMITS.MAX_DESCRIPTION_LENGTH
|
|
654
669
|
else error[: CONFIG.LIMITS.MAX_DESCRIPTION_LENGTH] + "..."
|
|
655
670
|
)
|
|
671
|
+
date_markup = [
|
|
672
|
+
{
|
|
673
|
+
"type": "text",
|
|
674
|
+
"text": f" at {str(datetime.datetime.now().strftime('%B %d, %Y %I:%M:%S %p'))}",
|
|
675
|
+
},
|
|
676
|
+
{"type": "hardBreak"},
|
|
677
|
+
]
|
|
656
678
|
|
|
657
679
|
error_markup = [
|
|
680
|
+
{
|
|
681
|
+
"type": "text",
|
|
682
|
+
"text": error,
|
|
683
|
+
"marks": [{"type": "code"}],
|
|
684
|
+
},
|
|
685
|
+
]
|
|
686
|
+
|
|
687
|
+
comment_markup = [
|
|
658
688
|
{
|
|
659
689
|
"type": "paragraph",
|
|
660
|
-
"content":
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
"type": "text",
|
|
669
|
-
"text": f" at {str(datetime.datetime.now().strftime('%B %d, %Y %I:%M:%S %p'))}",
|
|
670
|
-
},
|
|
671
|
-
{"type": "hardBreak"},
|
|
672
|
-
{
|
|
673
|
-
"type": "text",
|
|
674
|
-
"text": error,
|
|
675
|
-
"marks": [{"type": "code"}],
|
|
676
|
-
},
|
|
677
|
-
],
|
|
690
|
+
"content": (
|
|
691
|
+
[
|
|
692
|
+
{"type": "text", "text": "Error occures again in "},
|
|
693
|
+
]
|
|
694
|
+
+ self.__link_markup()
|
|
695
|
+
+ date_markup
|
|
696
|
+
+ error_markup
|
|
697
|
+
),
|
|
678
698
|
}
|
|
679
699
|
]
|
|
680
700
|
|
|
@@ -711,7 +731,7 @@ class Jira:
|
|
|
711
731
|
"type": "doc",
|
|
712
732
|
"version": 1,
|
|
713
733
|
"content": []
|
|
714
|
-
+ (
|
|
734
|
+
+ (comment_markup if error else [])
|
|
715
735
|
+ (self.__description_markup(additional_info) if additional_info else [])
|
|
716
736
|
+ (attach_markup if attachments else [])
|
|
717
737
|
+ (self.__metadata_markup(metadata) if metadata else []),
|
|
@@ -759,10 +779,10 @@ class Jira:
|
|
|
759
779
|
"""
|
|
760
780
|
issue_status = self.check_issue_status(existing_ticket["id"])
|
|
761
781
|
self._transition_types = self.__get_transtion_types(issue_id=existing_ticket["id"])
|
|
762
|
-
if issue_status.lower()
|
|
782
|
+
if issue_status.lower() not in ["to do", "open"]:
|
|
763
783
|
self.issue_transition(
|
|
764
784
|
ticket_id=existing_ticket["id"],
|
|
765
|
-
transition_id=self._transition_types
|
|
785
|
+
transition_id=self._transition_types.get("to do", "open"),
|
|
766
786
|
)
|
|
767
787
|
self.update_comment(
|
|
768
788
|
ticket_id=existing_ticket["id"],
|
|
@@ -780,7 +800,13 @@ class Jira:
|
|
|
780
800
|
posted_attachments = []
|
|
781
801
|
else:
|
|
782
802
|
posted_attachments = (
|
|
783
|
-
[
|
|
803
|
+
[
|
|
804
|
+
self.add_attachment(attachment, issue["id"])
|
|
805
|
+
for attachment in attachments
|
|
806
|
+
if os.path.exists(str(attachment))
|
|
807
|
+
]
|
|
808
|
+
if attachments
|
|
809
|
+
else []
|
|
784
810
|
)
|
|
785
811
|
|
|
786
812
|
self.update_comment(
|
|
@@ -817,7 +843,7 @@ class Jira:
|
|
|
817
843
|
summary=summary[:255].split("\n")[0],
|
|
818
844
|
description=description,
|
|
819
845
|
assignee=assignee_id,
|
|
820
|
-
issue_type=self._issue_types.get("bug"
|
|
846
|
+
issue_type=self._issue_types.get("bug") or self._issue_types.get("task", "support"),
|
|
821
847
|
labels=labels,
|
|
822
848
|
)
|
|
823
849
|
response = self.post_ticket(issue=issue)
|
|
@@ -834,7 +860,8 @@ class Jira:
|
|
|
834
860
|
ticket_id = ticket["id"]
|
|
835
861
|
if attachments:
|
|
836
862
|
for attachment in attachments:
|
|
837
|
-
|
|
863
|
+
if os.path.exists(str(attachment)):
|
|
864
|
+
self.add_attachment(attachment, ticket_id)
|
|
838
865
|
if self._webhook_url:
|
|
839
866
|
self.move_ticket_to_board(ticket_id)
|
|
840
867
|
return response
|
|
@@ -843,7 +870,7 @@ class Jira:
|
|
|
843
870
|
self,
|
|
844
871
|
exception: Optional[Exception] = None,
|
|
845
872
|
assignee: Optional[str] = None,
|
|
846
|
-
attachments:
|
|
873
|
+
attachments: Union[List, str, Path, None] = None,
|
|
847
874
|
metadata: Optional[dict] = None,
|
|
848
875
|
additional_info: Optional[str] = None,
|
|
849
876
|
) -> dict:
|
|
@@ -866,6 +893,9 @@ class Jira:
|
|
|
866
893
|
if attachments is None:
|
|
867
894
|
attachments = []
|
|
868
895
|
|
|
896
|
+
if isinstance(attachments, (str, Path)):
|
|
897
|
+
attachments = [str(attachments)]
|
|
898
|
+
|
|
869
899
|
if not isinstance(attachments, List):
|
|
870
900
|
logger.warning(f"Incorrect type of attachments: {type(attachments)}")
|
|
871
901
|
attachments = []
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import unittest
|
|
3
|
+
from unittest.mock import PropertyMock, patch
|
|
4
|
+
|
|
5
|
+
from t_bug_catcher.bug_catcher import BugCatcher
|
|
6
|
+
from t_bug_catcher.config import CONFIG
|
|
7
|
+
from t_bug_catcher.utils import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestBugCatcher(unittest.TestCase):
|
|
11
|
+
"""TestBugCatcher class."""
|
|
12
|
+
|
|
13
|
+
def setUp(self):
|
|
14
|
+
"""Set up."""
|
|
15
|
+
self.bug_catcher = BugCatcher()
|
|
16
|
+
|
|
17
|
+
def test_report_error_with_local_environment(self):
|
|
18
|
+
"""Test report_error function."""
|
|
19
|
+
with patch.object(logger, "warning") as mock_warning:
|
|
20
|
+
self.bug_catcher.report_error()
|
|
21
|
+
mock_warning.assert_called_once_with("Reporting an error is not supported in local environment.")
|
|
22
|
+
|
|
23
|
+
@patch.object(CONFIG, "ENVIRONMENT", "robocloud")
|
|
24
|
+
def test_report_error_with_no_configurations(self):
|
|
25
|
+
"""Test report_error function."""
|
|
26
|
+
with patch.object(logger, "warning") as mock_warning:
|
|
27
|
+
self.bug_catcher.report_error()
|
|
28
|
+
mock_warning.assert_called_once_with(
|
|
29
|
+
"Jira and BugSnag are not configured. Please configure them before reporting an error."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
def test_jira_configure(self):
|
|
33
|
+
"""Test jira_configure function."""
|
|
34
|
+
self.bug_catcher.configure.jira(
|
|
35
|
+
login=os.getenv("JIRA_LOGIN"),
|
|
36
|
+
api_token=os.getenv("JIRA_API_TOKEN"),
|
|
37
|
+
project_key=os.getenv("JIRA_PROJECT_KEY"),
|
|
38
|
+
)
|
|
39
|
+
assert self.bug_catcher.configure.is_jira_configured
|
|
40
|
+
|
|
41
|
+
def test_bugsnag_configure(self):
|
|
42
|
+
"""Test bugsnag_configure function."""
|
|
43
|
+
self.bug_catcher.configure.bugsnag(api_key=os.getenv("BUGSNAG_API_KEY"))
|
|
44
|
+
assert self.bug_catcher.configure.is_bugsnag_configured
|
|
45
|
+
|
|
46
|
+
def test_attach_file(self):
|
|
47
|
+
"""Test attach_file function."""
|
|
48
|
+
ex = Exception("some error")
|
|
49
|
+
self.bug_catcher.attach_file_to_exception(exception=ex, attachment="test.txt")
|
|
50
|
+
assert hasattr(ex, "custom_attachments")
|
|
51
|
+
|
|
52
|
+
@patch.object(CONFIG, "ENVIRONMENT", "robocloud")
|
|
53
|
+
def test_report_error(self):
|
|
54
|
+
"""Test report_error function."""
|
|
55
|
+
with patch.object(
|
|
56
|
+
self.bug_catcher, "_BugCatcher__configurator", new_callable=PropertyMock
|
|
57
|
+
) as mock_configurator:
|
|
58
|
+
patch.object(mock_configurator, "is_jira_configured", return_value=True)
|
|
59
|
+
patch.object(mock_configurator, "is_bugsnag_configured", return_value=True)
|
|
60
|
+
try:
|
|
61
|
+
raise Exception("Test exception")
|
|
62
|
+
except Exception as ex:
|
|
63
|
+
with patch.object(logger, "warning") as mock_warning:
|
|
64
|
+
self.bug_catcher.report_error(exception=ex)
|
|
65
|
+
actual_call = mock_warning.call_args
|
|
66
|
+
warning_message = actual_call[0][0]
|
|
67
|
+
self.assertTrue(warning_message.startswith("Failed to create Jira issue due to"))
|
|
68
|
+
self.assertEqual(mock_warning.call_count, 1)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|