Coverage for testrail_api_reporter/publishers/slack_sender.py: 35%

40 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-29 15:21 +0200

1# -*- coding: utf-8 -*- 

2""" Slack sender module """ 

3 

4import json 

5 

6import requests 

7 

8from ..utils.logger_config import setup_logger, DEFAULT_LOGGING_LEVEL 

9from ..utils.reporter_utils import format_error, check_captions_and_files 

10 

11 

12class SlackSender: 

13 """Slack sender class, see for details https://api.slack.com/messaging/webhooks""" 

14 

15 def __init__(self, hook_url=None, timeout=5, verify=True, logger=None, log_level=DEFAULT_LOGGING_LEVEL): 

16 """ 

17 General init 

18 

19 :param hook_url: url for Slack API hook, string, required 

20 :param timeout: timeout for message send, integer, optional 

21 :param verify: verification required, bool, optional 

22 :param logger: logger object, optional 

23 :param log_level: logging level, optional, by default, is 'logging.DEBUG' 

24 """ 

25 if not logger: 

26 self.___logger = setup_logger(name="SlackSender", log_file="SlackSender.log", level=log_level) 

27 else: 

28 self.___logger = logger 

29 self.___logger.debug("Initializing Slack Sender") 

30 if not hook_url: 

31 raise ValueError("No Slack hook url provided, aborted!") 

32 self.__hook_url = hook_url 

33 self.__timeout = timeout 

34 self.__verify = verify 

35 

36 @staticmethod 

37 def __prepare_attachments(files, captions): 

38 """ 

39 Prepares attachments 

40 

41 :param files: list of files (images) 

42 :param captions: list of captions for files, list of strings, if not provided, no captions will be added 

43 :return: list of dict with attachment info 

44 """ 

45 legacy_attachments = [] 

46 for j, file in enumerate(files): 

47 legacy_attachments.append( 

48 { 

49 "pretext": "----", 

50 "text": captions[j] if captions else "", 

51 "mrkdwn_in": ["text", "pretext"], 

52 "image_url": file, 

53 } 

54 ) 

55 return legacy_attachments 

56 

57 @staticmethod 

58 def __prepare_blocks(title): 

59 """ 

60 Prepares blocks 

61 

62 :param title: header message title 

63 :return: list of dict with blocks info 

64 """ 

65 return [{"type": "header", "text": {"type": "plain_text", "text": title, "emoji": True}}] 

66 

67 def __prepare_payload(self, title, files, captions): 

68 """ 

69 Prepares whole payload 

70 

71 :param title: header message title 

72 :param files: list of files (images) 

73 :param captions: list of captions for files, list of strings, if not provided, no captions will be added to 

74 :return: json with payload 

75 """ 

76 return json.dumps( 

77 { 

78 "attachments": self.__prepare_attachments(files=files, captions=captions), 

79 "blocks": self.__prepare_blocks(title=title), 

80 } 

81 ) 

82 

83 @staticmethod 

84 def __prepare_headers(): 

85 """ 

86 Prepares headers for request itself 

87 

88 :return: json with headers 

89 """ 

90 return {"Content-type": "application/json", "Accept": "text/plain"} 

91 

92 def send_message(self, files=None, captions=None, title="Test development & automation coverage report"): 

93 """ 

94 Send a message to Slack 

95 

96 :param files: list of urls of images 

97 :param captions: list of captions for files, list of strings, if not provided, no captions will be added 

98 :param title: header title of message 

99 :return: none 

100 """ 

101 # check params 

102 if not isinstance(files, list): 

103 raise ValueError("No file list for report provided, aborted!") 

104 captions = check_captions_and_files( 

105 captions=captions, 

106 files=files, 

107 debug=self.___logger.level == DEFAULT_LOGGING_LEVEL, 

108 logger=self.___logger, 

109 ) 

110 # Send to slack 

111 try: 

112 response = requests.post( 

113 url=self.__hook_url, 

114 data=self.__prepare_payload(title=title, files=files, captions=captions), 

115 timeout=self.__timeout, 

116 verify=self.__verify, 

117 headers=self.__prepare_headers(), 

118 ) 

119 if response.status_code != 200: 

120 raise ValueError( 

121 f"Message can't be sent! Error {response.status_code}: {response.text}: " 

122 f"{response.raise_for_status()}" 

123 ) 

124 self.___logger.debug("Message sent!") 

125 except Exception as error: 

126 raise ValueError(f"Message can't be sent!\nError{format_error(error)}") from error