async def _send(self, http_verb, api_url, req_args):
        """Sends the request out for transmission.

        Args:
            http_verb (str): The HTTP verb. e.g. 'GET' or 'POST'.
            api_url (str): The Slack API url. e.g. 'https://slack.com/api/chat.postMessage'
            req_args (dict): The request arguments to be attached to the request.
            e.g.
            {
                json: {
                    'attachments': [{"pretext": "pre-hello", "text": "text-world"}],
                    'channel': '#random'
                }
            }
        Returns:
            The response parsed into a SlackResponse object.
        """
        open_files = []
        files = req_args.pop("files", None)
        if files is not None:
            for k, v in files.items():
                if isinstance(v, str):
                    f = open(v.encode("utf-8", "ignore"), "rb")
                    open_files.append(f)
                    req_args["data"].update({k: f})
                else:
                    req_args["data"].update({k: v})

        if "params" in req_args:
            # True/False -> "1"/"0"
            req_args["params"] = convert_bool_to_0_or_1(req_args["params"])

        res = await self._request(
            http_verb=http_verb, api_url=api_url, req_args=req_args
        )

        for f in open_files:
            f.close()

        data = {
            "client": self,
            "http_verb": http_verb,
            "api_url": api_url,
            "req_args": req_args,
            "use_sync_aiohttp": self.use_sync_aiohttp,
        }
        return SlackResponse(**{**data, **res}).validate()
    async def _send(
        self, http_verb: str, api_url: str, req_args: dict
    ) -> SlackResponse:
        """Sends the request out for transmission.

        Args:
            http_verb (str): The HTTP verb. e.g. 'GET' or 'POST'.
            api_url (str): The Slack API url. e.g. 'https://slack.com/api/chat.postMessage'
            req_args (dict): The request arguments to be attached to the request.
            e.g.
            {
                json: {
                    'attachments': [{"pretext": "pre-hello", "text": "text-world"}],
                    'channel': '#random'
                }
            }
        Returns:
            The response parsed into a SlackResponse object.
        """
        open_files = _files_to_data(req_args)
        try:
            if "params" in req_args:
                # True/False -> "1"/"0"
                req_args["params"] = convert_bool_to_0_or_1(req_args["params"])

            res = await self._request(
                http_verb=http_verb, api_url=api_url, req_args=req_args
            )
        finally:
            for f in open_files:
                f.close()

        data = {
            "client": self,
            "http_verb": http_verb,
            "api_url": api_url,
            "req_args": req_args,
            "use_sync_aiohttp": self.use_sync_aiohttp,
        }
        return SlackResponse(**{**data, **res}).validate()
Beispiel #3
0
    def _urllib_api_call(
        self,
        *,
        token: str = None,
        url: str,
        query_params: Dict[str, str] = {},
        json_body: Dict = {},
        body_params: Dict[str, str] = {},
        files: Dict[str, io.BytesIO] = {},
        additional_headers: Dict[str, str] = {},
    ) -> SlackResponse:
        """Performs a Slack API request and returns the result.

        :param token: Slack API Token (either bot token or user token)
        :param url: a complete URL (e.g., https://www.slack.com/api/chat.postMessage)
        :param query_params: query string
        :param json_body: json data structure (it's still a dict at this point),
            if you give this argument, body_params and files will be skipped
        :param body_params: form params
        :param files: files to upload
        :param additional_headers: request headers to append
        :return: API response
        """

        files_to_close: List[BinaryIO] = []
        try:
            # True/False -> "1"/"0"
            query_params = convert_bool_to_0_or_1(query_params)
            body_params = convert_bool_to_0_or_1(body_params)

            if self._logger.level <= logging.DEBUG:

                def convert_params(values: dict) -> dict:
                    if not values or not isinstance(values, dict):
                        return {}
                    return {
                        k: ("(bytes)" if isinstance(v, bytes) else v)
                        for k, v in values.items()
                    }

                headers = {
                    k: "(redacted)" if k.lower() == "authorization" else v
                    for k, v in additional_headers.items()
                }
                self._logger.debug(
                    f"Sending a request - url: {url}, "
                    f"query_params: {convert_params(query_params)}, "
                    f"body_params: {convert_params(body_params)}, "
                    f"files: {convert_params(files)}, "
                    f"json_body: {json_body}, "
                    f"headers: {headers}")

            request_data = {}
            if files is not None and isinstance(files,
                                                dict) and len(files) > 0:
                if body_params:
                    for k, v in body_params.items():
                        request_data.update({k: v})

                for k, v in files.items():
                    if isinstance(v, str):
                        f: BinaryIO = open(v.encode("utf-8", "ignore"), "rb")
                        files_to_close.append(f)
                        request_data.update({k: f})
                    elif isinstance(v, (bytearray, bytes)):
                        request_data.update({k: io.BytesIO(v)})
                    else:
                        request_data.update({k: v})

            request_headers = self._build_urllib_request_headers(
                token=token or self.token,
                has_json=json is not None,
                has_files=files is not None,
                additional_headers=additional_headers,
            )
            request_args = {
                "headers": request_headers,
                "data": request_data,
                "params": body_params,
                "files": files,
                "json": json_body,
            }
            if query_params:
                q = urlencode(query_params)
                url = f"{url}&{q}" if "?" in url else f"{url}?{q}"

            response = self._perform_urllib_http_request(url=url,
                                                         args=request_args)
            if response.get("body"):
                try:
                    response_body_data: dict = json.loads(response["body"])
                except json.decoder.JSONDecodeError as e:
                    message = f"Failed to parse the response body: {str(e)}"
                    raise err.SlackApiError(message, response)
            else:
                response_body_data: dict = None

            if query_params:
                all_params = copy.copy(body_params)
                all_params.update(query_params)
            else:
                all_params = body_params
            request_args["params"] = all_params  # for backward-compatibility

            return SlackResponse(
                client=self,
                http_verb="POST",  # you can use POST method for all the Web APIs
                api_url=url,
                req_args=request_args,
                data=response_body_data,
                headers=dict(response["headers"]),
                status_code=response["status"],
                use_sync_aiohttp=False,
            ).validate()
        finally:
            for f in files_to_close:
                if not f.closed:
                    f.close()
def _build_body(original_body: Dict[str, any]) -> Dict[str, any]:
    body = {k: v for k, v in original_body.items() if v is not None}
    body = convert_bool_to_0_or_1(body)
    _parse_web_class_objects(body)
    return body
Beispiel #5
0
    def api_call(
            self,
            *,
            token: str = None,
            url: str,
            query_params: Dict[str, str] = dict(),
            json_body: Dict = dict(),
            body_params: Dict[str, str] = dict(),
            files: Dict[str, io.BytesIO] = dict(),
            additional_headers: Dict[str, str] = dict(),
    ) -> SlackResponse:
        """Performs a Slack API request and returns the result.

        :param token: Slack API Token (either bot token or user token)
        :param url: a complete URL (e.g., https://www.slack.com/api/chat.postMessage)
        :param query_params: query string
        :param json_body: json data structure (it's still a dict at this point),
            if you give this argument, body_params and files will be skipped
        :param body_params: form params
        :param files: files to upload
        :param additional_headers: request headers to append
        :return: API response
        """

        show_2020_01_deprecation(self._to_api_method(url))

        files_to_close: List[BinaryIO] = []
        try:
            # True/False -> "1"/"0"
            query_params = convert_bool_to_0_or_1(query_params)
            body_params = convert_bool_to_0_or_1(body_params)

            if self.logger.level <= logging.DEBUG:
                self.logger.debug(f"Slack API Request - url: {url}, "
                                  f"query_params: {query_params}, "
                                  f"json_body: {json_body}, "
                                  f"body_params: {body_params}, "
                                  f"files: {files}, "
                                  f"additional_headers: {additional_headers}")

            request_data = {}
            if files:
                if body_params:
                    for k, v in body_params.items():
                        request_data.update({k: v})

                for k, v in files.items():
                    if isinstance(v, str):
                        f: BinaryIO = open(v.encode("ascii", "ignore"), "rb")
                        files_to_close.append(f)
                        request_data.update({k: f})
                    else:
                        request_data.update({k: v})

            request_headers = self._build_request_headers(
                token=token or self.token,
                has_json=json is not None,
                has_files=files is not None,
                additional_headers=additional_headers,
            )
            request_args = {
                "headers": request_headers,
                "data": request_data,
                "params": body_params,
                "files": files,
                "json": json_body,
            }
            if query_params:
                q = urlencode(query_params)
                url = f"{url}&{q}" if "?" in url else f"{url}?{q}"

            response, response_body = self._perform_http_request(
                url=url, args=request_args)
            if response_body:
                response_body_data: dict = json.loads(response_body)
            else:
                response_body_data: dict = None

            if query_params:
                all_params = copy.copy(body_params)
                all_params.update(query_params)
            else:
                all_params = body_params
            request_args["params"] = all_params  # for backward-compatibility
            return SlackResponse(
                client=self.web_client,
                http_verb="POST",  # you can use POST method for all the Web APIs
                api_url=url,
                req_args=request_args,
                data=response_body_data,
                headers=dict(response.headers),
                status_code=response.status,
                use_sync_aiohttp=False,
            ).validate()
        finally:
            for f in files_to_close:
                if not f.closed:
                    f.close()