Coverage for chalicelib/core/collaborations/collaboration_slack.py: 38%

92 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:56 +0000

1from datetime import datetime 

2 

3from decouple import config 

4from fastapi import HTTPException, status 

5 

6import schemas 

7from chalicelib.core import webhook 

8from chalicelib.core.collaborations.collaboration_base import BaseCollaboration 

9import logging 

10 

11from chalicelib.utils import ssrf 

12from chalicelib.utils.log import sanitize 

13 

14logger = logging.getLogger(__name__) 

15 

16class Slack(BaseCollaboration): 

17 @classmethod 

18 def add(cls, tenant_id, data: schemas.AddCollaborationSchema): 

19 if webhook.exists_by_name(tenant_id=tenant_id, name=data.name, exclude_id=None, 19 ↛ 21line 19 didn't jump to line 21 because the condition on line 19 was never true

20 webhook_type=schemas.WebhookType.SLACK): 

21 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"name already exists.") 

22 try: 

23 hello = cls.say_hello(data.url) 

24 except ValueError as e: 

25 raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) 

26 if hello: 26 ↛ 27line 26 didn't jump to line 27 because the condition on line 26 was never true

27 return webhook.add(tenant_id=tenant_id, 

28 endpoint=data.url.unicode_string(), 

29 webhook_type=schemas.WebhookType.SLACK, 

30 name=data.name) 

31 return None 

32 

33 @classmethod 

34 def say_hello(cls, url): 

35 r = ssrf.post_json( 

36 endpoint=url, 

37 json_data={ 

38 "attachments": [ 

39 { 

40 "text": "Welcome to OpenReplay", 

41 "ts": datetime.now().timestamp(), 

42 } 

43 ] 

44 }) 

45 if r.status_code != 200: 45 ↛ 49line 45 didn't jump to line 49 because the condition on line 45 was always true

46 logger.error("slack integration failed") 

47 logger.error(sanitize(r.text)) 

48 return False 

49 return True 

50 

51 @classmethod 

52 def send_raw(cls, tenant_id, webhook_id, body): 

53 integration = cls.get_integration(tenant_id=tenant_id, integration_id=webhook_id) 

54 if integration is None: 

55 return {"errors": ["slack integration not found"]} 

56 try: 

57 r = ssrf.post_json( 

58 endpoint=integration["endpoint"], 

59 json_data=body, 

60 timeout=5) 

61 if r.status_code != 200: 

62 logger.warning(f"!! issue sending slack raw; webhookId:{webhook_id} code:{r.status_code}") 

63 logger.warning(sanitize(r.text)) 

64 return None 

65 except requests.exceptions.Timeout: 

66 logger.warning(f"!! Timeout sending slack raw webhookId:{webhook_id}") 

67 return None 

68 except Exception as e: 

69 logger.warning(f"!! Issue sending slack raw webhookId:{webhook_id}") 

70 logger.warning(sanitize(str(e))) 

71 return None 

72 return {"data": r.text} 

73 

74 @classmethod 

75 def send_batch(cls, tenant_id, webhook_id, attachments): 

76 integration = cls.get_integration(tenant_id=tenant_id, integration_id=webhook_id) 

77 if integration is None: 

78 return {"errors": ["slack integration not found"]} 

79 logger.debug(f"====> sending slack batch notification: {len(attachments)}") 

80 for i in range(0, len(attachments), 100): 

81 r = ssrf.post_json( 

82 endpoint=integration["endpoint"], 

83 json_data={"attachments": attachments[i:i + 100]}) 

84 if r.status_code != 200: 

85 logger.warning(f"!!!! something went wrong while sending slack batch; webhookId:{webhook_id} code:{r.status_code}") 

86 logger.warning(sanitize(r.text)) 

87 

88 @classmethod 

89 def __share(cls, tenant_id, integration_id, attachement, extra=None): 

90 if extra is None: 

91 extra = {} 

92 integration = cls.get_integration(tenant_id=tenant_id, integration_id=integration_id) 

93 if integration is None: 

94 return {"errors": ["slack integration not found"]} 

95 attachement["ts"] = datetime.now().timestamp() 

96 r = ssrf.post_json(endpoint=integration["endpoint"], json_data={"attachments": [attachement], **extra}) 

97 return r.text 

98 

99 @classmethod 

100 def share_session(cls, tenant_id, project_id, session_id, user, comment, project_name=None, integration_id=None): 

101 args = {"fallback": f"{user} has shared the below session!", 

102 "pretext": f"{user} has shared the below session!", 

103 "title": f"{config('SITE_URL')}/{project_id}/session/{session_id}", 

104 "title_link": f"{config('SITE_URL')}/{project_id}/session/{session_id}", 

105 "text": comment} 

106 data = cls.__share(tenant_id, integration_id, attachement=args) 

107 if "errors" in data: 

108 return data 

109 return {"data": data} 

110 

111 @classmethod 

112 def share_error(cls, tenant_id, project_id, error_id, user, comment, project_name=None, integration_id=None): 

113 args = {"fallback": f"{user} has shared the below error!", 

114 "pretext": f"{user} has shared the below error!", 

115 "title": f"{config('SITE_URL')}/{project_id}/errors/{error_id}", 

116 "title_link": f"{config('SITE_URL')}/{project_id}/errors/{error_id}", 

117 "text": comment} 

118 data = cls.__share(tenant_id, integration_id, attachement=args) 

119 if "errors" in data: 

120 return data 

121 return {"data": data} 

122 

123 @classmethod 

124 def get_integration(cls, tenant_id, integration_id=None): 

125 if integration_id is not None: 125 ↛ 129line 125 didn't jump to line 129 because the condition on line 125 was always true

126 return webhook.get_webhook(tenant_id=tenant_id, webhook_id=integration_id, 

127 webhook_type=schemas.WebhookType.SLACK) 

128 

129 integrations = webhook.get_by_type(tenant_id=tenant_id, webhook_type=schemas.WebhookType.SLACK) 

130 if integrations is None or len(integrations) == 0: 

131 return None 

132 return integrations[0]