docusign-esign-python-client icon indicating copy to clipboard operation
docusign-esign-python-client copied to clipboard

Python SDK API requests fail

Open schluta opened this issue 5 months ago • 7 comments

Yesterday I was having a lot of trouble getting the SDK to work. I could get good responses from the following command, so I thought that I had a good access token, however, any requests using the SDK's UserApi, AccountApi, or EnvelopesApi all would fail.

ApiClient.get_user_info(access_token)

Today I decided to try executing curl commands myself, and the access tokens were working just fine. I have not investigated to the root of the issue, but if I do not use

    api_client.host = BASE_PATH

in the following:

    api_client = ApiClient()
    api_client.set_base_path(BASE_PATH)
    with open(PRIVATE_KEY_PATH, 'r') as key_file:
        private_key_bytes = key_file.read()
    scopes = ['signature', 'impersonation']
    response = api_client.request_jwt_user_token(
        client_id=INTEGRATOR_KEY,
        user_id=USER_ID,
        oauth_host_name=OAUTH_HOST_NAME,
        private_key_bytes=private_key_bytes,
        expires_in=3600,
        scopes=scopes
    )
    #
    access_token = response.access_token
    api_client.host = BASE_PATH

Then all of my requests fail. I am not too sure what the difference between host and base_path are supposed to be. I am not sure what the config.host refers to. Interestingly, the only time the self.host member is used is when the call_api function is called. The relevant sections are below:

    def __init__(self, host=None, header_name=None, header_value=None, cookie=None, oauth_host_name=None, base_path=None):
        """
        Constructor of the class.
        """

        config = Configuration()
        self.rest_client = RESTClientObject(configuration=config)
        self.default_headers = {'X-DocuSign-SDK': 'Python'}
        if header_name is not None:
            self.default_headers[header_name] = header_value
        if host is None:
            self.host = config.host
        elif host == "":
            raise ArgumentException("basePath cannot be empty")
        else:
            self.host = host
def __call_api(self, resource_path, method,
                   path_params=None, query_params=None, header_params=None,
                   body=None, post_params=None, files=None,
                   response_type=None, auth_settings=None, callback=None,
                   _return_http_data_only=None, collection_formats=None, _preload_content=True,
                   _request_timeout=None):
        """
        :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without
                                 reading/decoding response data. Default is True. 
        :return: 
        """

        # header parameters
        header_params = header_params or {}
        header_params.update(self.default_headers)
        if self.cookie:
            header_params['Cookie'] = self.cookie
        if header_params:
            header_params = self.sanitize_for_serialization(header_params)
            header_params = dict(self.parameters_to_tuples(header_params,
                                                           collection_formats))

        # path parameters
        if path_params:
            path_params = self.sanitize_for_serialization(path_params)
            path_params = self.parameters_to_tuples(path_params,
                                                    collection_formats)
            for k, v in path_params:
                resource_path = resource_path.replace(
                    '{%s}' % k, quote(str(v), safe=""))

        # query parameters
        if query_params:
            query_params = self.sanitize_for_serialization(query_params)
            query_params = self.parameters_to_tuples(query_params,
                                                     collection_formats)

        # post parameters
        if post_params or files:
            post_params = self.prepare_post_parameters(post_params, files)
            post_params = self.sanitize_for_serialization(post_params)
            post_params = self.parameters_to_tuples(post_params,
                                                    collection_formats)

        # auth setting
        self.update_params_for_auth(header_params, query_params, auth_settings)

        # body
        if body:
            body = self.sanitize_for_serialization(body)

        # request url
        url = self.host + resource_path

I am not really sure how anyone is really using this SDK. To me this looks like a bug or oversight where the base_path should be used. Some debug functions or prints would be awesome.

I accidentally stumbled upon this "fix" at this link in the Instantiating and configuring the ApiClient for making API calls section.

schluta avatar Aug 22 '25 15:08 schluta