def test_user_agent_customization_issue_769(self): client = WebClient( base_url="http://localhost:8888", token="xoxb-user-agent this_is test", user_agent_prefix="this_is", user_agent_suffix="test", ) resp = client.api_test() self.assertTrue(resp["ok"])
class TestWebClient(unittest.TestCase): def setUp(self): setup_mock_web_api_server(self) self.client = WebClient( token="xoxb-api_test", base_url="http://localhost:8888", ) def tearDown(self): cleanup_mock_web_api_server(self) pattern_for_language = re.compile("python/(\\S+)", re.IGNORECASE) pattern_for_package_identifier = re.compile("slackclient/(\\S+)") def test_subsequent_requests_with_a_session_succeeds(self): resp = self.client.api_test() assert resp["ok"] resp = self.client.api_test() assert resp["ok"] def test_api_calls_include_user_agent(self): self.client.token = "xoxb-api_test" resp = self.client.api_test() self.assertEqual(200, resp.status_code) def test_builtin_api_methods_send_json(self): self.client.token = "xoxb-api_test" resp = self.client.api_test(msg="bye") self.assertEqual(200, resp.status_code) self.assertEqual("bye", resp["args"]["msg"]) def test_requests_can_be_paginated(self): self.client.token = "xoxb-users_list_pagination" users = [] for page in self.client.users_list(limit=2): users = users + page["members"] self.assertTrue(len(users) == 4) def test_response_can_be_paginated_multiple_times(self): self.client.token = "xoxb-conversations_list_pagination" # This test suite verifies the changes in #521 work as expected response = self.client.conversations_list(limit=1) ids = [] for page in response: ids.append(page["channels"][0]["id"]) self.assertEqual(ids, ["C1", "C2", "C3"]) # The second iteration starting with page 2 # (page1 is already cached in `response`) self.client.token = "xoxb-conversations_list_pagination2" ids = [] for page in response: ids.append(page["channels"][0]["id"]) self.assertEqual(ids, ["C1", "C2", "C3"]) def test_request_pagination_stops_when_next_cursor_is_missing(self): self.client.token = "xoxb-users_list_pagination_1" users = [] for page in self.client.users_list(limit=2): users = users + page["members"] self.assertTrue(len(users) == 2) def test_json_can_only_be_sent_with_post_requests(self): with self.assertRaises(err.SlackRequestError): self.client.api_call("fake.method", http_verb="GET", json={}) def test_slack_api_error_is_raised_on_unsuccessful_responses(self): self.client.token = "xoxb-api_test_false" with self.assertRaises(err.SlackApiError): self.client.api_test() self.client.token = "xoxb-500" with self.assertRaises(err.SlackApiError): self.client.api_test() def test_slack_api_rate_limiting_exception_returns_retry_after(self): self.client.token = "xoxb-rate_limited" try: self.client.api_test() except err.SlackApiError as slack_api_error: self.assertFalse(slack_api_error.response["ok"]) self.assertEqual(429, slack_api_error.response.status_code) self.assertEqual(30, int(slack_api_error.response.headers["Retry-After"])) def test_the_api_call_files_argument_creates_the_expected_data(self): self.client.token = "xoxb-users_setPhoto" resp = self.client.users_setPhoto( image="tests/slack_sdk_fixture/slack_logo.png" ) self.assertEqual(200, resp.status_code) def test_issue_560_bool_in_params_sync(self): self.client.token = "xoxb-conversations_list" self.client.conversations_list(exclude_archived=1) # ok self.client.conversations_list(exclude_archived="true") # ok self.client.conversations_list(exclude_archived=True) # ok def test_issue_690_oauth_v2_access(self): self.client.token = "" resp = self.client.oauth_v2_access( client_id="111.222", client_secret="secret", code="codeeeeeeeeee" ) self.assertIsNone(resp["error"]) with self.assertRaises(err.SlackApiError): self.client.oauth_v2_access( client_id="999.999", client_secret="secret", code="codeeeeeeeeee" ) def test_issue_690_oauth_access(self): self.client.token = "" resp = self.client.oauth_access( client_id="111.222", client_secret="secret", code="codeeeeeeeeee" ) self.assertIsNone(resp["error"]) with self.assertRaises(err.SlackApiError): self.client.oauth_access( client_id="999.999", client_secret="secret", code="codeeeeeeeeee" ) def test_issue_705_no_param_request_pagination(self): self.client.token = "xoxb-users_list_pagination" users = [] for page in self.client.users_list(): users = users + page["members"] self.assertTrue(len(users) == 4) def test_token_param(self): client = WebClient(base_url="http://localhost:8888") with self.assertRaises(err.SlackApiError): client.users_list() resp = client.users_list(token="xoxb-users_list_pagination") self.assertIsNone(resp["error"]) with self.assertRaises(err.SlackApiError): client.users_list() def test_timeout_issue_712(self): client = WebClient(base_url="http://localhost:8888", timeout=1) with self.assertRaises(socket.timeout): client.users_list(token="xoxb-timeout") def test_html_response_body_issue_718(self): client = WebClient(base_url="http://localhost:8888") try: client.users_list(token="xoxb-html_response") self.fail("SlackApiError expected here") except err.SlackApiError as e: self.assertTrue( str(e).startswith( "Received a response in a non-JSON format: <!DOCTYPE HTML PUBLIC" ), e, ) def test_user_agent_customization_issue_769(self): client = WebClient( base_url="http://localhost:8888", token="xoxb-user-agent this_is test", user_agent_prefix="this_is", user_agent_suffix="test", ) resp = client.api_test() self.assertTrue(resp["ok"]) def test_default_team_id(self): client = WebClient(base_url="http://localhost:8888", team_id="T_DEFAULT") resp = client.users_list(token="xoxb-users_list_pagination") self.assertIsNone(resp["error"])
class SlackMonitor(Monitor): """ Create a monitoring service that alerts on Task failures / completion in a Slack channel """ def __init__(self, slack_api_token, channel, message_prefix=None, filters=None): # type: (str, str, Optional[str], Optional[List[Callable[[Task], bool]]]) -> () """ Create a Slack Monitoring object. It will alert on any Task/Experiment that failed or completed :param slack_api_token: Slack bot API Token. Token should start with "xoxb-" :param channel: Name of the channel to post alerts to :param message_prefix: optional message prefix to add before any message posted For example: message_prefix="Hey <!here>," :param filters: An optional collection of callables that will be passed a Task object and return True/False if it should be filtered away """ super(SlackMonitor, self).__init__() self.channel = "{}".format(channel[1:] if channel[0] == "#" else channel) self.slack_client = WebClient(token=slack_api_token) self.min_num_iterations = 0 self.filters = filters or list() self.status_alerts = [ "failed", ] self.include_manual_experiments = False self.include_archived = False self.verbose = False self._channel_id = None self._message_prefix = "{} ".format( message_prefix) if message_prefix else "" self.check_credentials() def check_credentials(self): # type: () -> () """ Check we have the correct credentials for the slack channel """ self.slack_client.api_test() # Find channel ID channels = [] cursor = None while True: response = self.slack_client.conversations_list(cursor=cursor) channels.extend(response.data["channels"]) cursor = response.data["response_metadata"].get("next_cursor") if not cursor: break channel_id = [ channel_info.get("id") for channel_info in channels if channel_info.get("name") == self.channel ] if not channel_id: raise ValueError( "Error: Could not locate channel name '{}'".format( self.channel)) # test bot permission (join channel) self._channel_id = channel_id[0] self.slack_client.conversations_join(channel=self._channel_id) def post_message(self, message, retries=1, wait_period=10.0): # type: (str, int, float) -> () """ Post message on our slack channel :param message: Message to be sent (markdown style) :param retries: Number of retries before giving up :param wait_period: wait between retries in seconds """ for i in range(retries): if i != 0: sleep(wait_period) try: self.slack_client.chat_postMessage( channel=self._channel_id, blocks=[ dict(type="section", text={ "type": "mrkdwn", "text": message }) ], ) return except SlackApiError as e: print( 'While trying to send message: "\n{}\n"\nGot an error: {}'. format(message, e.response["error"])) def get_query_parameters(self): # type: () -> dict """ Return the query parameters for the monitoring. :return dict: Example dictionary: {'status': ['failed'], 'order_by': ['-last_update']} """ filter_tags = list() if self.include_archived else ["-archived"] if not self.include_manual_experiments: filter_tags.append("-development") return dict(status=self.status_alerts, order_by=["-last_update"], system_tags=filter_tags) def process_task(self, task): """ # type: (Task) -> () Called on every Task that we monitor. This is where we send the Slack alert :return: None """ # skipping failed tasks with low number of iterations if self.min_num_iterations and task.get_last_iteration( ) < self.min_num_iterations: print("Skipping {} experiment id={}, number of iterations {} < {}". format(task.status, task.id, task.get_last_iteration(), self.min_num_iterations)) return if any(f(task) for f in self.filters): if self.verbose: print("Experiment id={} {} did not pass all filters".format( task.id, task.status)) return print('Experiment id={} {}, raising alert on channel "{}"'.format( task.id, task.status, self.channel)) console_output = task.get_reported_console_output(number_of_reports=3) message = "{}Experiment ID <{}|{}> *{}*\nProject: *{}* - Name: *{}*\n" "```\n{}\n```".format( self._message_prefix, task.get_output_log_web_page(), task.id, task.status, task.get_project_name(), task.name, ("\n".join(console_output))[-2048:], ) self.post_message(message, retries=5)