t-bug-catcher 0.2.0__tar.gz → 0.2.2__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.0 → t_bug_catcher-0.2.2}/PKG-INFO +1 -1
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/setup.cfg +1 -1
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/setup.py +1 -1
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/__init__.py +1 -1
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/bug_catcher.py +3 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/bug_snag.py +27 -23
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/jira.py +98 -48
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher.egg-info/PKG-INFO +1 -1
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/MANIFEST.in +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/README.rst +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/pyproject.toml +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/requirements.txt +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/config.py +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/exceptions.py +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/utils/__init__.py +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/utils/common.py +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/utils/logger.py +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher/workitems.py +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher.egg-info/SOURCES.txt +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher.egg-info/dependency_links.txt +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher.egg-info/not-zip-safe +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher.egg-info/requires.txt +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/t_bug_catcher.egg-info/top_level.txt +0 -0
- {t_bug_catcher-0.2.0 → t_bug_catcher-0.2.2}/tests/test_t_bug_catcher.py +0 -0
|
@@ -28,6 +28,7 @@ class Configurator:
|
|
|
28
28
|
api_token: str,
|
|
29
29
|
project_key: str,
|
|
30
30
|
webhook_url: Optional[str] = None,
|
|
31
|
+
default_assignee: Optional[str] = None,
|
|
31
32
|
):
|
|
32
33
|
"""Configures the JiraPoster and BugSnag classes.
|
|
33
34
|
|
|
@@ -36,6 +37,7 @@ class Configurator:
|
|
|
36
37
|
api_token (str): The API token for the Jira account.
|
|
37
38
|
project_key (str): The key of the Jira project.
|
|
38
39
|
webhook_url (str, optional): The webhook URL for the Jira project. Defaults to None.
|
|
40
|
+
default_assignee (str, optional): The default assignee for the Jira project. Defaults to None.
|
|
39
41
|
|
|
40
42
|
Returns:
|
|
41
43
|
None
|
|
@@ -45,6 +47,7 @@ class Configurator:
|
|
|
45
47
|
api_token=api_token,
|
|
46
48
|
project_key=project_key,
|
|
47
49
|
webhook_url=webhook_url,
|
|
50
|
+
default_assignee=default_assignee,
|
|
48
51
|
)
|
|
49
52
|
|
|
50
53
|
def bugsnag(self, api_key: str):
|
|
@@ -28,30 +28,34 @@ class BugSnag:
|
|
|
28
28
|
Returns:
|
|
29
29
|
bool: True if the configuration was successful, False otherwise.
|
|
30
30
|
"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
31
|
+
try:
|
|
32
|
+
bugsnag.configure(api_key=api_key, release_stage=CONFIG.ENVIRONMENT, auto_notify=False)
|
|
33
|
+
bugsnag.add_metadata_tab(
|
|
34
|
+
"Metadata",
|
|
35
|
+
{
|
|
36
|
+
"run_url": variables.get("processRunUrl", ""),
|
|
37
|
+
"run_by": variables.get("userEmail", ""),
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
response = requests.request(
|
|
41
|
+
"POST",
|
|
42
|
+
"https://otlp.bugsnag.com/v1/traces",
|
|
43
|
+
headers={
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
"Bugsnag-Api-Key": api_key,
|
|
46
|
+
"Bugsnag-Payload-Version": "4",
|
|
47
|
+
"Bugsnag-Sent-At": f"{datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')}",
|
|
48
|
+
"Bugsnag-Span-Sampling": "True",
|
|
49
|
+
},
|
|
50
|
+
data='{"message": "test"}',
|
|
51
|
+
)
|
|
52
|
+
if response.status_code not in [200, 201, 202, 204]:
|
|
53
|
+
logger.warning(f"Error connecting to Bugsnag: {response.text}")
|
|
54
|
+
return False
|
|
55
|
+
return True
|
|
56
|
+
except Exception as ex:
|
|
57
|
+
logger.warning(f"Failed to configure Bugsnag: {ex}")
|
|
53
58
|
return False
|
|
54
|
-
return True
|
|
55
59
|
|
|
56
60
|
def report_error(self, exception: Optional[Exception] = None, metadata: Optional[dict] = None):
|
|
57
61
|
"""Sends an error to BugSnag.
|
|
@@ -52,6 +52,7 @@ class Jira:
|
|
|
52
52
|
self._project_key = None
|
|
53
53
|
self._webhook_url = None
|
|
54
54
|
self._auth = None
|
|
55
|
+
self._default_assignee = None
|
|
55
56
|
|
|
56
57
|
@staticmethod
|
|
57
58
|
def _is_json_response(response) -> bool:
|
|
@@ -88,7 +89,9 @@ class Jira:
|
|
|
88
89
|
f"{exc_message}Status Code: {response.status_code}, " f"Headers: {response.headers}"
|
|
89
90
|
)
|
|
90
91
|
|
|
91
|
-
def config(
|
|
92
|
+
def config(
|
|
93
|
+
self, login, api_token, project_key, webhook_url: Optional[str] = None, default_assignee: Optional[str] = None
|
|
94
|
+
) -> bool:
|
|
92
95
|
"""Sets the webhook URL for the Jira project.
|
|
93
96
|
|
|
94
97
|
Args:
|
|
@@ -96,22 +99,28 @@ class Jira:
|
|
|
96
99
|
api_token (str): The API token for the Jira account.
|
|
97
100
|
project_key (str): The key of the Jira project.
|
|
98
101
|
webhook_url (str): The webhook URL for the Jira project.
|
|
102
|
+
default_assignee (str, optional): The default assignee for the Jira project. Defaults to None.
|
|
99
103
|
|
|
100
104
|
Returns:
|
|
101
105
|
bool: True if the configuration was successful, False otherwise.
|
|
102
106
|
"""
|
|
103
|
-
self._project_key = project_key
|
|
104
|
-
if not webhook_url:
|
|
105
|
-
logger.warning("No JIRA webhook URL provided. All issues will be posted to backlog.")
|
|
106
|
-
self._webhook_url = webhook_url
|
|
107
|
-
self._auth = self._authenticate(login, api_token)
|
|
108
107
|
try:
|
|
109
|
-
self.
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
self._project_key = project_key
|
|
109
|
+
self._default_assignee = default_assignee
|
|
110
|
+
if not webhook_url:
|
|
111
|
+
logger.warning("No JIRA webhook URL provided. All issues will be posted to backlog.")
|
|
112
|
+
self._webhook_url = webhook_url
|
|
113
|
+
self._auth = self._authenticate(login, api_token)
|
|
114
|
+
try:
|
|
115
|
+
self.get_current_user()
|
|
116
|
+
except BadRequestError:
|
|
117
|
+
logger.warning("Failed to authenticate to Jira or incorrect project key.")
|
|
118
|
+
return False
|
|
119
|
+
self._issue_types = self.__get_issue_types()
|
|
120
|
+
return True
|
|
121
|
+
except Exception as ex:
|
|
122
|
+
logger.warning(f"Failed to configure Jira: {ex}")
|
|
112
123
|
return False
|
|
113
|
-
self._issue_types = self.__get_issue_types()
|
|
114
|
-
return True
|
|
115
124
|
|
|
116
125
|
def _authenticate(self, login, api_token) -> HTTPBasicAuth:
|
|
117
126
|
"""Function to authenticate the user with the provided username and API token.
|
|
@@ -395,12 +404,8 @@ class Jira:
|
|
|
395
404
|
"text": "Run link: ",
|
|
396
405
|
"marks": [{"type": "strong"}],
|
|
397
406
|
},
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
"text": CONFIG.ENVIRONMENT,
|
|
401
|
-
"marks": ([] + self.__link_markup()),
|
|
402
|
-
},
|
|
403
|
-
],
|
|
407
|
+
]
|
|
408
|
+
+ self.__link_markup(),
|
|
404
409
|
}
|
|
405
410
|
]
|
|
406
411
|
if CONFIG.ENVIRONMENT != "local"
|
|
@@ -520,19 +525,39 @@ class Jira:
|
|
|
520
525
|
link_markup = {
|
|
521
526
|
variables.get("environment"): [
|
|
522
527
|
{
|
|
523
|
-
"type": "
|
|
524
|
-
"
|
|
525
|
-
|
|
526
|
-
|
|
528
|
+
"type": "text",
|
|
529
|
+
"text": variables.get("processRunUrl"),
|
|
530
|
+
"marks": [
|
|
531
|
+
{
|
|
532
|
+
"type": "link",
|
|
533
|
+
"attrs": {"href": variables.get("processRunUrl")},
|
|
534
|
+
},
|
|
535
|
+
{"type": "underline"},
|
|
536
|
+
],
|
|
537
|
+
}
|
|
527
538
|
],
|
|
528
539
|
"robocloud": [
|
|
529
540
|
{
|
|
530
|
-
"type": "
|
|
531
|
-
"
|
|
532
|
-
|
|
533
|
-
|
|
541
|
+
"type": "text",
|
|
542
|
+
"text": "https://cloud.robocorp.com",
|
|
543
|
+
"marks": [
|
|
544
|
+
{
|
|
545
|
+
"type": "link",
|
|
546
|
+
"attrs": {"href": CONFIG.RC_RUN_LINK},
|
|
547
|
+
},
|
|
548
|
+
{"type": "underline"},
|
|
549
|
+
],
|
|
550
|
+
}
|
|
551
|
+
],
|
|
552
|
+
"local": [
|
|
553
|
+
{
|
|
554
|
+
"type": "text",
|
|
555
|
+
"text": "local run",
|
|
556
|
+
"marks": [
|
|
557
|
+
{"type": "underline"},
|
|
558
|
+
],
|
|
559
|
+
}
|
|
534
560
|
],
|
|
535
|
-
"local": [{"type": "underline"}],
|
|
536
561
|
}
|
|
537
562
|
return link_markup[CONFIG.ENVIRONMENT]
|
|
538
563
|
|
|
@@ -644,28 +669,33 @@ class Jira:
|
|
|
644
669
|
if len(error) < CONFIG.LIMITS.MAX_DESCRIPTION_LENGTH
|
|
645
670
|
else error[: CONFIG.LIMITS.MAX_DESCRIPTION_LENGTH] + "..."
|
|
646
671
|
)
|
|
672
|
+
date_markup = [
|
|
673
|
+
{
|
|
674
|
+
"type": "text",
|
|
675
|
+
"text": f" at {str(datetime.datetime.now().strftime('%B %d, %Y %I:%M:%S %p'))}",
|
|
676
|
+
},
|
|
677
|
+
{"type": "hardBreak"},
|
|
678
|
+
]
|
|
647
679
|
|
|
648
680
|
error_markup = [
|
|
681
|
+
{
|
|
682
|
+
"type": "text",
|
|
683
|
+
"text": error,
|
|
684
|
+
"marks": [{"type": "code"}],
|
|
685
|
+
},
|
|
686
|
+
]
|
|
687
|
+
|
|
688
|
+
comment_markup = [
|
|
649
689
|
{
|
|
650
690
|
"type": "paragraph",
|
|
651
|
-
"content":
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
"type": "text",
|
|
660
|
-
"text": f" at {str(datetime.datetime.now().strftime('%B %d, %Y %I:%M:%S %p'))}",
|
|
661
|
-
},
|
|
662
|
-
{"type": "hardBreak"},
|
|
663
|
-
{
|
|
664
|
-
"type": "text",
|
|
665
|
-
"text": error,
|
|
666
|
-
"marks": [{"type": "code"}],
|
|
667
|
-
},
|
|
668
|
-
],
|
|
691
|
+
"content": (
|
|
692
|
+
[
|
|
693
|
+
{"type": "text", "text": "Error occures again in "},
|
|
694
|
+
]
|
|
695
|
+
+ self.__link_markup()
|
|
696
|
+
+ date_markup
|
|
697
|
+
+ error_markup
|
|
698
|
+
),
|
|
669
699
|
}
|
|
670
700
|
]
|
|
671
701
|
|
|
@@ -702,7 +732,7 @@ class Jira:
|
|
|
702
732
|
"type": "doc",
|
|
703
733
|
"version": 1,
|
|
704
734
|
"content": []
|
|
705
|
-
+ (
|
|
735
|
+
+ (comment_markup if error else [])
|
|
706
736
|
+ (self.__description_markup(additional_info) if additional_info else [])
|
|
707
737
|
+ (attach_markup if attachments else [])
|
|
708
738
|
+ (self.__metadata_markup(metadata) if metadata else []),
|
|
@@ -771,7 +801,13 @@ class Jira:
|
|
|
771
801
|
posted_attachments = []
|
|
772
802
|
else:
|
|
773
803
|
posted_attachments = (
|
|
774
|
-
[
|
|
804
|
+
[
|
|
805
|
+
self.add_attachment(attachment, issue["id"])
|
|
806
|
+
for attachment in attachments
|
|
807
|
+
if os.path.exists(str(attachment))
|
|
808
|
+
]
|
|
809
|
+
if attachments
|
|
810
|
+
else []
|
|
775
811
|
)
|
|
776
812
|
|
|
777
813
|
self.update_comment(
|
|
@@ -825,7 +861,8 @@ class Jira:
|
|
|
825
861
|
ticket_id = ticket["id"]
|
|
826
862
|
if attachments:
|
|
827
863
|
for attachment in attachments:
|
|
828
|
-
|
|
864
|
+
if os.path.exists(str(attachment)):
|
|
865
|
+
self.add_attachment(attachment, ticket_id)
|
|
829
866
|
if self._webhook_url:
|
|
830
867
|
self.move_ticket_to_board(ticket_id)
|
|
831
868
|
return response
|
|
@@ -834,7 +871,7 @@ class Jira:
|
|
|
834
871
|
self,
|
|
835
872
|
exception: Optional[Exception] = None,
|
|
836
873
|
assignee: Optional[str] = None,
|
|
837
|
-
attachments:
|
|
874
|
+
attachments: Union[List, str, Path, None] = None,
|
|
838
875
|
metadata: Optional[dict] = None,
|
|
839
876
|
additional_info: Optional[str] = None,
|
|
840
877
|
) -> dict:
|
|
@@ -857,6 +894,9 @@ class Jira:
|
|
|
857
894
|
if attachments is None:
|
|
858
895
|
attachments = []
|
|
859
896
|
|
|
897
|
+
if isinstance(attachments, (str, Path)):
|
|
898
|
+
attachments = [str(attachments)]
|
|
899
|
+
|
|
860
900
|
if not isinstance(attachments, List):
|
|
861
901
|
logger.warning(f"Incorrect type of attachments: {type(attachments)}")
|
|
862
902
|
attachments = []
|
|
@@ -886,6 +926,7 @@ class Jira:
|
|
|
886
926
|
return existing_ticket
|
|
887
927
|
|
|
888
928
|
assignee_id = None
|
|
929
|
+
assignee = assignee if assignee else self._default_assignee
|
|
889
930
|
if assignee:
|
|
890
931
|
try:
|
|
891
932
|
assignee_id = self.__get_assignee(assignee)
|
|
@@ -906,6 +947,7 @@ class Jira:
|
|
|
906
947
|
description=description,
|
|
907
948
|
assignee_id=assignee_id,
|
|
908
949
|
attachments=attachments,
|
|
950
|
+
labels=["bug_catcher"],
|
|
909
951
|
)
|
|
910
952
|
return response
|
|
911
953
|
except Exception as ex:
|
|
@@ -939,6 +981,13 @@ class Jira:
|
|
|
939
981
|
)
|
|
940
982
|
return existing_ticket
|
|
941
983
|
|
|
984
|
+
assignee_id = None
|
|
985
|
+
if self._default_assignee:
|
|
986
|
+
try:
|
|
987
|
+
assignee_id = self.__get_assignee(self._default_assignee)
|
|
988
|
+
except Exception as ex:
|
|
989
|
+
logger.info(f"Failed to get assignee {self._default_assignee} due to: {ex}")
|
|
990
|
+
|
|
942
991
|
description = self.__create_description_markup(
|
|
943
992
|
exc_type=exc_type,
|
|
944
993
|
exc_value=exc_value,
|
|
@@ -949,6 +998,7 @@ class Jira:
|
|
|
949
998
|
response = self.__create_new_ticket(
|
|
950
999
|
summary=summary,
|
|
951
1000
|
description=description,
|
|
1001
|
+
assignee_id=assignee_id,
|
|
952
1002
|
labels=["fatal_error"],
|
|
953
1003
|
)
|
|
954
1004
|
return response
|
|
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
|