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"])
Exemplo n.º 3
0
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)