docusign_esign.client.api_client

Docusign eSignature REST API

The Docusign eSignature REST API provides you with a powerful, convenient, and simple Web services API for interacting with Docusign. # noqa: E501

OpenAPI spec version: v2.1 Contact: devcenter@docusign.com Generated by: https://github.com/swagger-api/swagger-codegen.git

  1# coding: utf-8
  2
  3"""
  4    Docusign eSignature REST API
  5
  6    The Docusign eSignature REST API provides you with a powerful, convenient, and simple Web services API for interacting with Docusign.  # noqa: E501
  7
  8    OpenAPI spec version: v2.1
  9    Contact: devcenter@docusign.com
 10    Generated by: https://github.com/swagger-api/swagger-codegen.git
 11"""
 12
 13
 14from __future__ import absolute_import
 15
 16import os
 17import re
 18import json
 19import mimetypes
 20import threading
 21import base64
 22import math
 23import jwt
 24
 25from datetime import date, datetime
 26from time import time
 27
 28# python 2 and python 3 compatibility library
 29from six import PY3, integer_types, iteritems, text_type
 30from six.moves.urllib.parse import quote
 31
 32from docusign_esign import client
 33from docusign_esign import models
 34
 35from .configuration import Configuration
 36from .api_exception import ApiException, ArgumentException
 37from .api_response import RESTClientObject, RESTResponse
 38from .auth.oauth import OAuthUserInfo, OAuthToken, OAuth, Account, Organization, Link
 39
 40class ApiClient(object):
 41    """
 42    Generic API client for Swagger client library builds.
 43
 44    Swagger generic API client. This client handles the client-
 45    server communication, and is invariant across implementations. Specifics of
 46    the methods and models for each application are generated from the Swagger
 47    templates.
 48
 49    NOTE: This class is auto generated by the swagger code generator program.
 50    Ref: https://github.com/swagger-api/swagger-codegen
 51    Do not edit the class manually.
 52
 53    :param host: The base path for the server to call.
 54    :param header_name: a header to pass when making calls to the API.
 55    :param header_value: a header value to pass when making calls to the API.
 56    """
 57
 58    PRIMITIVE_TYPES = (float, bool, bytes, text_type) + integer_types
 59    NATIVE_TYPES_MAPPING = {
 60        'int': int,
 61        'long': int if PY3 else long,
 62        'float': float,
 63        'str': str,
 64        'bool': bool,
 65        'date': date,
 66        'datetime': datetime,
 67        'object': object,
 68    }
 69
 70    OAUTH_TYPES = (OAuthToken.__name__, OAuthUserInfo.__name__, Account.__name__, Organization.__name__, Link.__name__)
 71
 72    def __init__(self, host=None, header_name=None, header_value=None, cookie=None, oauth_host_name=None, base_path=None):
 73        """
 74        Constructor of the class.
 75        """
 76
 77        config = Configuration()
 78        self.rest_client = RESTClientObject(configuration=config)
 79        self.default_headers = {'X-DocuSign-SDK': 'Python'}
 80        if header_name is not None:
 81            self.default_headers[header_name] = header_value
 82        if host is None:
 83            self.host = config.host
 84        elif host == "":
 85            raise ArgumentException("basePath cannot be empty")
 86        else:
 87            self.host = host
 88
 89        self.cookie = cookie
 90        self.base_path = base_path
 91        self.set_oauth_host_name(oauth_host_name)
 92
 93        # Set default User-Agent.
 94        self.user_agent = config.user_agent
 95
 96    @property
 97    def user_agent(self):
 98        """
 99        Gets user agent.
100        """
101        return self.default_headers['User-Agent']
102
103    @user_agent.setter
104    def user_agent(self, value):
105        """
106        Sets user agent.
107        """
108        self.default_headers['User-Agent'] = value
109
110    def set_default_header(self, header_name, header_value):
111        self.default_headers[header_name] = header_value
112
113    def __call_api(self, resource_path, method,
114                   path_params=None, query_params=None, header_params=None,
115                   body=None, post_params=None, files=None,
116                   response_type=None, auth_settings=None, callback=None,
117                   _return_http_data_only=None, collection_formats=None, _preload_content=True,
118                   _request_timeout=None):
119        """
120        :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without
121                                 reading/decoding response data. Default is True. 
122        :return: 
123        """
124
125        # header parameters
126        header_params = header_params or {}
127        header_params.update(self.default_headers)
128        if self.cookie:
129            header_params['Cookie'] = self.cookie
130        if header_params:
131            header_params = self.sanitize_for_serialization(header_params)
132            header_params = dict(self.parameters_to_tuples(header_params,
133                                                           collection_formats))
134
135        # path parameters
136        if path_params:
137            path_params = self.sanitize_for_serialization(path_params)
138            path_params = self.parameters_to_tuples(path_params,
139                                                    collection_formats)
140            for k, v in path_params:
141                resource_path = resource_path.replace(
142                    '{%s}' % k, quote(str(v), safe=""))
143
144        # query parameters
145        if query_params:
146            query_params = self.sanitize_for_serialization(query_params)
147            query_params = self.parameters_to_tuples(query_params,
148                                                     collection_formats)
149
150        # post parameters
151        if post_params or files:
152            post_params = self.prepare_post_parameters(post_params, files)
153            post_params = self.sanitize_for_serialization(post_params)
154            post_params = self.parameters_to_tuples(post_params,
155                                                    collection_formats)
156
157        # auth setting
158        self.update_params_for_auth(header_params, query_params, auth_settings)
159
160        # body
161        if body:
162            body = self.sanitize_for_serialization(body)
163
164        # request url
165        url = self.host + resource_path
166
167        # perform request and return response
168        response_data = self.request(method, url,
169                                     query_params=query_params,
170                                     headers=header_params,
171                                     post_params=post_params, body=body,
172                                     _preload_content=_preload_content,
173                                     _request_timeout=_request_timeout)
174
175        self.last_response = response_data
176
177        return_data = response_data
178        if _preload_content:
179            r = RESTResponse(response_data)
180
181            # deserialize response data
182            if response_type:
183                # In the python 3, the response.data is bytes.
184                # we need to decode it to string.
185                if PY3 and response_type != "file":
186                    try:
187                        r.data = r.data.decode('utf8', 'replace')
188                    except (UnicodeDecodeError, AttributeError):
189                        pass
190                
191                if response_type == "file":
192                    return_data = r.data
193                else:
194                    return_data = self.deserialize(r, response_type)
195            else:
196                return_data = None
197
198        if callback:
199            if _return_http_data_only:
200                callback(return_data)
201            else:
202                callback((return_data, response_data.status, response_data.getheaders()))
203        elif _return_http_data_only:
204            return (return_data)
205        else:
206            return (return_data, response_data.status, response_data.getheaders())
207
208    def sanitize_for_serialization(self, obj):
209        """
210        Builds a JSON POST object.
211
212        If obj is None, return None.
213        If obj is str, int, long, float, bool, return directly.
214        If obj is datetime.datetime, datetime.date
215            convert to string in iso8601 format.
216        If obj is list, sanitize each element in the list.
217        If obj is dict, return the dict.
218        If obj is swagger model, return the properties dict.
219
220        :param obj: The data to serialize.
221        :return: The serialized form of data.
222        """
223        if obj is None:
224            return None
225        elif isinstance(obj, self.PRIMITIVE_TYPES):
226            return obj
227        elif isinstance(obj, list):
228            return [self.sanitize_for_serialization(sub_obj)
229                    for sub_obj in obj]
230        elif isinstance(obj, tuple):
231            return tuple(self.sanitize_for_serialization(sub_obj)
232                         for sub_obj in obj)
233        elif isinstance(obj, (datetime, date)):
234            return obj.isoformat()
235
236        if isinstance(obj, dict):
237            obj_dict = obj
238        else:
239            # Convert model obj to dict except
240            # attributes `swagger_types`, `attribute_map`
241            # and attributes which value is not None.
242            # Convert attribute name to json key in
243            # model definition for request.
244            obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
245                        for attr, _ in iteritems(obj.swagger_types)
246                        if getattr(obj, attr) is not None}
247
248        return {key: self.sanitize_for_serialization(val)
249                for key, val in iteritems(obj_dict)}
250
251    def deserialize(self, response, response_type):
252        """
253        Deserializes response into an object.
254
255        :param response: RESTResponse object to be deserialized.
256        :param response_type: class literal for
257            deserialized object, or string of class name.
258
259        :return: deserialized object.
260        """
261        # handle file type response
262        if response_type == "file":
263            return response.data
264
265        # fetch data from response object
266        try:
267            data = json.loads(response.data)
268        except ValueError:
269            data = response.data
270
271        return self.__deserialize(data, response_type)
272
273    def __deserialize(self, data, klass):
274        """
275        Deserializes dict, list, str into an object.
276
277        :param data: dict, list or str.
278        :param klass: class literal, or string of class name.
279
280        :return: object.
281        """
282        if data is None:
283            return None
284
285        if type(klass) == str:
286            if klass.startswith('list['):
287                sub_kls = re.match(r'list\[(.*)\]', klass).group(1)
288                return [self.__deserialize(sub_data, sub_kls)
289                        for sub_data in data]
290
291            if klass.startswith('dict('):
292                sub_kls = re.match(r'dict\(([^,]*), (.*)\)', klass).group(2)
293                return {k: self.__deserialize(v, sub_kls)
294                        for k, v in iteritems(data)}
295
296            # convert str to class
297            if klass in self.NATIVE_TYPES_MAPPING:
298                klass = self.NATIVE_TYPES_MAPPING[klass]
299            else:
300                if klass in self.OAUTH_TYPES:
301                    klass = getattr(client, klass)
302                else:
303                    klass = getattr(models, klass)
304
305        if klass in self.PRIMITIVE_TYPES:
306            return self.__deserialize_primitive(data, klass)
307        elif klass == object:
308            return self.__deserialize_object(data)
309        elif klass == date:
310            return self.__deserialize_date(data)
311        elif klass == datetime:
312            return self.__deserialize_datatime(data)
313        else:
314            return self.__deserialize_model(data, klass)
315
316    def call_api(self, resource_path, method,
317                 path_params=None, query_params=None, header_params=None,
318                 body=None, post_params=None, files=None,
319                 response_type=None, auth_settings=None, callback=None,
320                 _return_http_data_only=None, collection_formats=None, _preload_content=True,
321                 _request_timeout=None):
322        """
323        Makes the HTTP request (synchronous) and return the deserialized data.
324        To make an async request, define a function for callback.
325
326        :param resource_path: Path to method endpoint.
327        :param method: Method to call.
328        :param path_params: Path parameters in the url.
329        :param query_params: Query parameters in the url.
330        :param header_params: Header parameters to be
331            placed in the request header.
332        :param body: Request body.
333        :param post_params dict: Request post form parameters,
334            for `application/x-www-form-urlencoded`, `multipart/form-data`.
335        :param auth_settings list: Auth Settings names for the request.
336        :param response: Response data type.
337        :param files dict: key -> filename, value -> filepath,
338            for `multipart/form-data`.
339        :param callback function: Callback function for asynchronous request.
340            If provide this parameter,
341            the request will be called asynchronously.
342        :param _return_http_data_only: response data without head status code and headers
343        :param collection_formats: dict of collection formats for path, query,
344            header, and post parameters.
345        :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without
346                                 reading/decoding response data. Default is True.
347        :param _request_timeout: timeout setting for this request. If one number provided, it will be total request
348                                 timeout. It can also be a pair (tuple) of (connection, read) timeouts.
349        :return:
350            If provide parameter callback,
351            the request will be called asynchronously.
352            The method will return the request thread.
353            If parameter callback is None,
354            then the method will return the response directly.
355        """
356        if callback is None:
357            return self.__call_api(resource_path, method,
358                                   path_params, query_params, header_params,
359                                   body, post_params, files,
360                                   response_type, auth_settings, callback,
361                                   _return_http_data_only, collection_formats, _preload_content, _request_timeout)
362        else:
363            thread = threading.Thread(target=self.__call_api,
364                                      args=(resource_path, method,
365                                            path_params, query_params,
366                                            header_params, body,
367                                            post_params, files,
368                                            response_type, auth_settings,
369                                            callback, _return_http_data_only,
370                                            collection_formats, _preload_content, _request_timeout))
371        thread.start()
372        return thread
373
374    def request(self, method, url, query_params=None, headers=None,
375                post_params=None, body=None, _preload_content=True, _request_timeout=None):
376        """
377        Makes the HTTP request using RESTClient.
378        """
379        if method == "GET":
380            return self.rest_client.GET(url,
381                                        query_params=query_params,
382                                        _preload_content=_preload_content,
383                                        _request_timeout=_request_timeout,
384                                        headers=headers)
385        elif method == "HEAD":
386            return self.rest_client.HEAD(url,
387                                         query_params=query_params,
388                                         _preload_content=_preload_content,
389                                         _request_timeout=_request_timeout,
390                                         headers=headers)
391        elif method == "OPTIONS":
392            return self.rest_client.OPTIONS(url,
393                                            query_params=query_params,
394                                            headers=headers,
395                                            post_params=post_params,
396                                            _preload_content=_preload_content,
397                                            _request_timeout=_request_timeout,
398                                            body=body)
399        elif method == "POST":
400            return self.rest_client.POST(url,
401                                         query_params=query_params,
402                                         headers=headers,
403                                         post_params=post_params,
404                                         _preload_content=_preload_content,
405                                         _request_timeout=_request_timeout,
406                                         body=body)
407        elif method == "PUT":
408            return self.rest_client.PUT(url,
409                                        query_params=query_params,
410                                        headers=headers,
411                                        post_params=post_params,
412                                        _preload_content=_preload_content,
413                                        _request_timeout=_request_timeout,
414                                        body=body)
415        elif method == "PATCH":
416            return self.rest_client.PATCH(url,
417                                          query_params=query_params,
418                                          headers=headers,
419                                          post_params=post_params,
420                                          _preload_content=_preload_content,
421                                          _request_timeout=_request_timeout,
422                                          body=body)
423        elif method == "DELETE":
424            return self.rest_client.DELETE(url,
425                                           query_params=query_params,
426                                           headers=headers,
427                                           _preload_content=_preload_content,
428                                           _request_timeout=_request_timeout,
429                                           body=body)
430        else:
431            raise ValueError(
432                "http method must be `GET`, `HEAD`, `OPTIONS`,"
433                " `POST`, `PATCH`, `PUT` or `DELETE`."
434            )
435
436    def parameters_to_tuples(self, params, collection_formats):
437        """
438        Get parameters as list of tuples, formatting collections.
439
440        :param params: Parameters as dict or list of two-tuples
441        :param dict collection_formats: Parameter collection formats
442        :return: Parameters as list of tuples, collections formatted
443        """
444        new_params = []
445        if collection_formats is None:
446            collection_formats = {}
447        for k, v in iteritems(params) if isinstance(params, dict) else params:
448            if k in collection_formats:
449                collection_format = collection_formats[k]
450                if collection_format == 'multi':
451                    new_params.extend((k, value) for value in v)
452                else:
453                    if collection_format == 'ssv':
454                        delimiter = ' '
455                    elif collection_format == 'tsv':
456                        delimiter = '\t'
457                    elif collection_format == 'pipes':
458                        delimiter = '|'
459                    else:  # csv is the default
460                        delimiter = ','
461                    new_params.append(
462                        (k, delimiter.join(str(value) for value in v)))
463            else:
464                new_params.append((k, v))
465        return new_params
466
467    def prepare_post_parameters(self, post_params=None, files=None):
468        """
469        Builds form parameters.
470
471        :param post_params: Normal form parameters.
472        :param files: File parameters.
473        :return: Form parameters with files.
474        """
475        params = []
476
477        if post_params:
478            params = post_params
479
480        if files:
481            for k, v in iteritems(files):
482                if not v:
483                    continue
484                file_names = v if type(v) is list else [v]
485                for n in file_names:
486                    with open(n, 'rb') as f:
487                        filename = os.path.basename(f.name)
488                        filedata = f.read()
489                        mimetype = mimetypes.\
490                            guess_type(filename)[0] or 'application/octet-stream'
491                        params.append(tuple([k, tuple([filename, filedata, mimetype])]))
492
493        return params
494
495    def select_header_accept(self, accepts):
496        """
497        Returns `Accept` based on an array of accepts provided.
498
499        :param accepts: List of headers.
500        :return: Accept (e.g. application/json).
501        """
502        if not accepts:
503            return
504
505        accepts = [x.lower() for x in accepts]
506
507        if 'application/json' in accepts:
508            return 'application/json'
509        else:
510            return ', '.join(accepts)
511
512    def select_header_content_type(self, content_types):
513        """
514        Returns `Content-Type` based on an array of content_types provided.
515
516        :param content_types: List of content-types.
517        :return: Content-Type (e.g. application/json).
518        """
519        if not content_types:
520            return 'application/json'
521
522        content_types = [x.lower() for x in content_types]
523
524        if 'application/json' in content_types or '*/*' in content_types:
525            return 'application/json'
526        else:
527            return content_types[0]
528
529    def update_params_for_auth(self, headers, querys, auth_settings):
530        """
531        Updates header and query params based on authentication setting.
532
533        :param headers: Header parameters dict to be updated.
534        :param querys: Query parameters tuple list to be updated.
535        :param auth_settings: Authentication setting identifiers list.
536        """
537        config = Configuration()
538
539        if not auth_settings:
540            return
541
542        for auth in auth_settings:
543            auth_setting = config.auth_settings().get(auth)
544            if auth_setting:
545                if not auth_setting['value']:
546                    continue
547                elif auth_setting['in'] == 'header':
548                    headers[auth_setting['key']] = auth_setting['value']
549                elif auth_setting['in'] == 'query':
550                    querys.append((auth_setting['key'], auth_setting['value']))
551                else:
552                    raise ValueError(
553                        'Authentication token must be in `query` or `header`'
554                    )
555
556    def __deserialize_primitive(self, data, klass):
557        """
558        Deserializes string to primitive type.
559
560        :param data: str.
561        :param klass: class literal.
562
563        :return: int, long, float, str, bool.
564        """
565        try:
566            return klass(data)
567        except UnicodeEncodeError:
568            return unicode(data)
569        except TypeError:
570            return data
571
572    def __deserialize_object(self, value):
573        """
574        Return a original value.
575
576        :return: object.
577        """
578        return value
579
580    def __deserialize_date(self, string):
581        """
582        Deserializes string to date.
583
584        :param string: str.
585        :return: date.
586        """
587        try:
588            from dateutil.parser import parse
589            return parse(string).date()
590        except ImportError:
591            return string
592        except ValueError:
593            raise ApiException(
594                status=0,
595                reason="Failed to parse `{0}` into a date object".format(string)
596            )
597
598    def __deserialize_datatime(self, string):
599        """
600        Deserializes string to datetime.
601
602        The string should be in iso8601 datetime format.
603
604        :param string: str.
605        :return: datetime.
606        """
607        try:
608            from dateutil.parser import parse
609            return parse(string)
610        except ImportError:
611            return string
612        except ValueError:
613            raise ApiException(
614                status=0,
615                reason=(
616                    "Failed to parse `{0}` into a datetime object"
617                    .format(string)
618                )
619            )
620
621    def __deserialize_model(self, data, klass):
622        """
623        Deserializes list or dict to model.
624
625        :param data: dict, list.
626        :param klass: class literal.
627        :return: model object.
628        """
629        instance = klass()
630
631        if not instance.swagger_types:
632            return data
633
634        for attr, attr_type in iteritems(instance.swagger_types):
635            if data is not None \
636               and instance.attribute_map[attr] in data \
637               and isinstance(data, (list, dict)):
638                value = data[instance.attribute_map[attr]]
639                setattr(instance, attr, self.__deserialize(value, attr_type))
640
641        return instance
642
643    def request_jwt_user_token(self, client_id, user_id, oauth_host_name, private_key_bytes, expires_in,
644                               scopes=(OAuth.SCOPE_SIGNATURE,)):
645        """
646        Request JWT User Token
647        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
648        :param user_id: Docusign user Id to be impersonated
649        :param oauth_host_name: Docusign OAuth host name
650        :param private_key_bytes: the byte contents of the RSA private key
651        :param expires_in: number of seconds remaining before the JWT assertion is considered as invalid
652        :param scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any
653        advanced scope.
654        :return: OAuthToken object
655        """
656        if not private_key_bytes:
657            raise ArgumentException("Private key not supplied or is invalid!")
658        if not user_id:
659            raise ArgumentException("User Id not supplied or is invalid!")
660        if not oauth_host_name:
661            raise ArgumentException("oAuthBasePath cannot be empty")
662
663        now = math.floor(time())
664        later = now + (expires_in * 1)
665        claim = {"iss": client_id, "sub": user_id, "aud": oauth_host_name, "iat": now, "exp": later,
666                 "scope": " ".join(scopes)}
667        token = jwt.encode(payload=claim, key=private_key_bytes, algorithm='RS256')
668        response = self.request("POST", "https://" + oauth_host_name + "/oauth/token",
669                                headers=self.sanitize_for_serialization(
670                                    {"Content-Type": "application/x-www-form-urlencoded"}),
671                                post_params=self.sanitize_for_serialization(
672                                    {"assertion": token, "grant_type": OAuth.GRANT_TYPE_JWT}))
673
674        response_data = json.loads(response.data)
675
676        if 'token_type' in response_data and 'access_token' in response_data:
677            self.set_default_header("Authorization", response_data['token_type'] + " " + response_data['access_token'])
678        else:
679            raise ApiException(status=response.status,
680                               reason="Error while requesting server, received a non successful HTTP code {}"
681                                       " with response Body: {}".format(response.status, response.data)
682                               )
683
684        return self.deserialize(response=response, response_type=OAuthToken)
685
686    def request_jwt_application_token(self, client_id, oauth_host_name, private_key_bytes, expires_in,
687                                      scopes=(OAuth.SCOPE_SIGNATURE,)):
688        """
689        Request JWT Application Token
690        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
691        :param oauth_host_name: Docusign OAuth host name
692        :param private_key_bytes: the byte contents of the RSA private key
693        :param expires_in: number of seconds remaining before the JWT assertion is considered as invalid
694        :param scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any
695        advanced scope.
696        :return: OAuthToken object
697        """
698
699        if not private_key_bytes:
700            raise ArgumentException("Private key not supplied or is invalid!")
701        if not oauth_host_name:
702            raise ArgumentException("oAuthBasePath cannot be empty")
703
704        now = math.floor(time())
705        later = now + (expires_in * 1)
706        claim = {"iss": client_id, "aud": oauth_host_name, "iat": now, "exp": later,
707                 "scope": " ".join(scopes)}
708        token = jwt.encode(payload=claim, key=private_key_bytes, algorithm='RS256')
709
710        response = self.request("POST", "https://" + oauth_host_name + "/oauth/token",
711                                headers=self.sanitize_for_serialization(
712                                    {"Content-Type": "application/x-www-form-urlencoded"}),
713                                post_params=self.sanitize_for_serialization(
714                                    {"assertion": token, "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer"}))
715        response_data = json.loads(response.data)
716
717        if 'token_type' in response_data and 'access_token' in response_data:
718            self.set_default_header("Authorization", response_data['token_type'] + " " + response_data['access_token'])
719        else:
720            ApiException(status=response.status,
721                         reason="Error while requesting server, received a non successful HTTP code {}"
722                                " with response Body: {}".format(response.status, response.data)
723                         )
724
725        return self.deserialize(response=response, response_type=OAuthToken)
726
727    def get_user_info(self, access_token):
728        """
729        Get User Info method takes the accessToken to retrieve User Account Data.
730        :param access_token:
731        :return: The User Info model.
732        """
733        if not access_token:
734            raise ArgumentException("Cannot find a valid access token."
735                                    " Make sure OAuth is configured before you try again.")
736        if not self.oauth_host_name:
737            raise ArgumentException("oAuthBasePath cannot be empty")
738
739        resource_path = '/oauth/userinfo'
740        headers = {"Authorization": "Bearer " + access_token}
741
742        response = self.request("GET", "https://" + self.oauth_host_name + resource_path, headers=headers)
743        return self.deserialize(response=response, response_type=OAuthUserInfo)
744
745    def generate_access_token(self, client_id, client_secret, code):
746        """
747        GenerateAccessToken will exchange the authorization code for an access token and refresh tokens.
748        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
749        :param client_secret: The secret key you generated when you set up the integration in Docusign Admin console.
750        :param code: The authorization code
751        :return: OAuthToken object
752        """
753        if not client_id or not client_secret or not code:
754            raise ArgumentException
755        url = "https://{0}/oauth/token".format(self.oauth_host_name)
756        integrator_and_secret_key = b"Basic " + base64.b64encode(str.encode("{}:{}".format(client_id, client_secret)))
757        headers = {
758            "Authorization": integrator_and_secret_key.decode("utf-8"),
759            "Content-Type": "application/x-www-form-urlencoded",
760        }
761        post_params = self.sanitize_for_serialization({
762            "grant_type": "authorization_code",
763            "code": code
764        })
765        response = self.rest_client.POST(url, headers=headers, post_params=post_params)
766
767        return self.deserialize(response=response, response_type=OAuthToken)
768
769    def set_base_path(self, base_path):
770        """
771        :param base_path:
772        :return:
773        """
774        self.base_path = base_path
775
776    def set_oauth_host_name(self, oauth_host_name=None):
777        """
778        :param oauth_host_name:
779        :return:
780        """
781        if oauth_host_name:
782            self.oauth_host_name = oauth_host_name
783            return
784
785        # Derive OAuth Base Path if not given
786        if self.base_path is None or self.base_path.startswith("https://demo") or self.base_path.startswith("http://demo") or self.base_path.startswith("https://apps-d") or self.base_path.startswith("http://apps-d"):
787            self.oauth_host_name = OAuth.DEMO_OAUTH_BASE_PATH
788        elif self.base_path.startswith("https://stage") or self.base_path.startswith("http://stage") or self.base_path.startswith("https://apps-s") or self.base_path.startswith("http://apps-s"):
789            self.oauth_host_name = OAuth.STAGE_OAUTH_BASE_PATH
790        else:
791            self.oauth_host_name = OAuth.PRODUCTION_OAUTH_BASE_PATH
792
793    def set_access_token(self, token_obj):
794        """
795
796        :param token_obj: 
797        :return: 
798        """
799        self.default_headers['Authorization'] = token_obj.access_token
800
801    def get_authorization_uri(self, client_id, scopes, redirect_uri, response_type, state=None):
802        """
803        Helper method to configure the OAuth accessCode/implicit flow parameters
804        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
805        :param scopes: The list of requested scopes.  Client applications may be scoped to a limited set of system access.
806        :param redirect_uri: This determines where to deliver the response containing the authorization code
807        :param response_type: Determines the response type of the authorization request, NOTE: these response types are
808        mutually exclusive for a client application. A public/native client application may only request a response type
809         of "token". A private/trusted client application may only request a response type of "code".
810        :param state: Allows for arbitrary state that may be useful to your application. The value in this parameter
811        will be round-tripped along with the response so you can make sure it didn't change.
812        :return: string
813        """
814        if not self.oauth_host_name:
815            self.oauth_host_name = self.get_oauth_host_name()
816        scopes = " ".join(scopes) if scopes else ""
817        uri = "https://{}/oauth/auth?response_type={}&scope={}&client_id={}&redirect_uri={}"
818        if state:
819            uri += "&state={}"
820        return uri.format(self.oauth_host_name, response_type, quote(scopes), client_id, redirect_uri, state)
821
822    def get_oauth_host_name(self):
823        """
824        :return: string
825        """
826        if not self.oauth_host_name:
827            self.set_oauth_host_name()
828
829        return self.oauth_host_name
class ApiClient:
 41class ApiClient(object):
 42    """
 43    Generic API client for Swagger client library builds.
 44
 45    Swagger generic API client. This client handles the client-
 46    server communication, and is invariant across implementations. Specifics of
 47    the methods and models for each application are generated from the Swagger
 48    templates.
 49
 50    NOTE: This class is auto generated by the swagger code generator program.
 51    Ref: https://github.com/swagger-api/swagger-codegen
 52    Do not edit the class manually.
 53
 54    :param host: The base path for the server to call.
 55    :param header_name: a header to pass when making calls to the API.
 56    :param header_value: a header value to pass when making calls to the API.
 57    """
 58
 59    PRIMITIVE_TYPES = (float, bool, bytes, text_type) + integer_types
 60    NATIVE_TYPES_MAPPING = {
 61        'int': int,
 62        'long': int if PY3 else long,
 63        'float': float,
 64        'str': str,
 65        'bool': bool,
 66        'date': date,
 67        'datetime': datetime,
 68        'object': object,
 69    }
 70
 71    OAUTH_TYPES = (OAuthToken.__name__, OAuthUserInfo.__name__, Account.__name__, Organization.__name__, Link.__name__)
 72
 73    def __init__(self, host=None, header_name=None, header_value=None, cookie=None, oauth_host_name=None, base_path=None):
 74        """
 75        Constructor of the class.
 76        """
 77
 78        config = Configuration()
 79        self.rest_client = RESTClientObject(configuration=config)
 80        self.default_headers = {'X-DocuSign-SDK': 'Python'}
 81        if header_name is not None:
 82            self.default_headers[header_name] = header_value
 83        if host is None:
 84            self.host = config.host
 85        elif host == "":
 86            raise ArgumentException("basePath cannot be empty")
 87        else:
 88            self.host = host
 89
 90        self.cookie = cookie
 91        self.base_path = base_path
 92        self.set_oauth_host_name(oauth_host_name)
 93
 94        # Set default User-Agent.
 95        self.user_agent = config.user_agent
 96
 97    @property
 98    def user_agent(self):
 99        """
100        Gets user agent.
101        """
102        return self.default_headers['User-Agent']
103
104    @user_agent.setter
105    def user_agent(self, value):
106        """
107        Sets user agent.
108        """
109        self.default_headers['User-Agent'] = value
110
111    def set_default_header(self, header_name, header_value):
112        self.default_headers[header_name] = header_value
113
114    def __call_api(self, resource_path, method,
115                   path_params=None, query_params=None, header_params=None,
116                   body=None, post_params=None, files=None,
117                   response_type=None, auth_settings=None, callback=None,
118                   _return_http_data_only=None, collection_formats=None, _preload_content=True,
119                   _request_timeout=None):
120        """
121        :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without
122                                 reading/decoding response data. Default is True. 
123        :return: 
124        """
125
126        # header parameters
127        header_params = header_params or {}
128        header_params.update(self.default_headers)
129        if self.cookie:
130            header_params['Cookie'] = self.cookie
131        if header_params:
132            header_params = self.sanitize_for_serialization(header_params)
133            header_params = dict(self.parameters_to_tuples(header_params,
134                                                           collection_formats))
135
136        # path parameters
137        if path_params:
138            path_params = self.sanitize_for_serialization(path_params)
139            path_params = self.parameters_to_tuples(path_params,
140                                                    collection_formats)
141            for k, v in path_params:
142                resource_path = resource_path.replace(
143                    '{%s}' % k, quote(str(v), safe=""))
144
145        # query parameters
146        if query_params:
147            query_params = self.sanitize_for_serialization(query_params)
148            query_params = self.parameters_to_tuples(query_params,
149                                                     collection_formats)
150
151        # post parameters
152        if post_params or files:
153            post_params = self.prepare_post_parameters(post_params, files)
154            post_params = self.sanitize_for_serialization(post_params)
155            post_params = self.parameters_to_tuples(post_params,
156                                                    collection_formats)
157
158        # auth setting
159        self.update_params_for_auth(header_params, query_params, auth_settings)
160
161        # body
162        if body:
163            body = self.sanitize_for_serialization(body)
164
165        # request url
166        url = self.host + resource_path
167
168        # perform request and return response
169        response_data = self.request(method, url,
170                                     query_params=query_params,
171                                     headers=header_params,
172                                     post_params=post_params, body=body,
173                                     _preload_content=_preload_content,
174                                     _request_timeout=_request_timeout)
175
176        self.last_response = response_data
177
178        return_data = response_data
179        if _preload_content:
180            r = RESTResponse(response_data)
181
182            # deserialize response data
183            if response_type:
184                # In the python 3, the response.data is bytes.
185                # we need to decode it to string.
186                if PY3 and response_type != "file":
187                    try:
188                        r.data = r.data.decode('utf8', 'replace')
189                    except (UnicodeDecodeError, AttributeError):
190                        pass
191                
192                if response_type == "file":
193                    return_data = r.data
194                else:
195                    return_data = self.deserialize(r, response_type)
196            else:
197                return_data = None
198
199        if callback:
200            if _return_http_data_only:
201                callback(return_data)
202            else:
203                callback((return_data, response_data.status, response_data.getheaders()))
204        elif _return_http_data_only:
205            return (return_data)
206        else:
207            return (return_data, response_data.status, response_data.getheaders())
208
209    def sanitize_for_serialization(self, obj):
210        """
211        Builds a JSON POST object.
212
213        If obj is None, return None.
214        If obj is str, int, long, float, bool, return directly.
215        If obj is datetime.datetime, datetime.date
216            convert to string in iso8601 format.
217        If obj is list, sanitize each element in the list.
218        If obj is dict, return the dict.
219        If obj is swagger model, return the properties dict.
220
221        :param obj: The data to serialize.
222        :return: The serialized form of data.
223        """
224        if obj is None:
225            return None
226        elif isinstance(obj, self.PRIMITIVE_TYPES):
227            return obj
228        elif isinstance(obj, list):
229            return [self.sanitize_for_serialization(sub_obj)
230                    for sub_obj in obj]
231        elif isinstance(obj, tuple):
232            return tuple(self.sanitize_for_serialization(sub_obj)
233                         for sub_obj in obj)
234        elif isinstance(obj, (datetime, date)):
235            return obj.isoformat()
236
237        if isinstance(obj, dict):
238            obj_dict = obj
239        else:
240            # Convert model obj to dict except
241            # attributes `swagger_types`, `attribute_map`
242            # and attributes which value is not None.
243            # Convert attribute name to json key in
244            # model definition for request.
245            obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
246                        for attr, _ in iteritems(obj.swagger_types)
247                        if getattr(obj, attr) is not None}
248
249        return {key: self.sanitize_for_serialization(val)
250                for key, val in iteritems(obj_dict)}
251
252    def deserialize(self, response, response_type):
253        """
254        Deserializes response into an object.
255
256        :param response: RESTResponse object to be deserialized.
257        :param response_type: class literal for
258            deserialized object, or string of class name.
259
260        :return: deserialized object.
261        """
262        # handle file type response
263        if response_type == "file":
264            return response.data
265
266        # fetch data from response object
267        try:
268            data = json.loads(response.data)
269        except ValueError:
270            data = response.data
271
272        return self.__deserialize(data, response_type)
273
274    def __deserialize(self, data, klass):
275        """
276        Deserializes dict, list, str into an object.
277
278        :param data: dict, list or str.
279        :param klass: class literal, or string of class name.
280
281        :return: object.
282        """
283        if data is None:
284            return None
285
286        if type(klass) == str:
287            if klass.startswith('list['):
288                sub_kls = re.match(r'list\[(.*)\]', klass).group(1)
289                return [self.__deserialize(sub_data, sub_kls)
290                        for sub_data in data]
291
292            if klass.startswith('dict('):
293                sub_kls = re.match(r'dict\(([^,]*), (.*)\)', klass).group(2)
294                return {k: self.__deserialize(v, sub_kls)
295                        for k, v in iteritems(data)}
296
297            # convert str to class
298            if klass in self.NATIVE_TYPES_MAPPING:
299                klass = self.NATIVE_TYPES_MAPPING[klass]
300            else:
301                if klass in self.OAUTH_TYPES:
302                    klass = getattr(client, klass)
303                else:
304                    klass = getattr(models, klass)
305
306        if klass in self.PRIMITIVE_TYPES:
307            return self.__deserialize_primitive(data, klass)
308        elif klass == object:
309            return self.__deserialize_object(data)
310        elif klass == date:
311            return self.__deserialize_date(data)
312        elif klass == datetime:
313            return self.__deserialize_datatime(data)
314        else:
315            return self.__deserialize_model(data, klass)
316
317    def call_api(self, resource_path, method,
318                 path_params=None, query_params=None, header_params=None,
319                 body=None, post_params=None, files=None,
320                 response_type=None, auth_settings=None, callback=None,
321                 _return_http_data_only=None, collection_formats=None, _preload_content=True,
322                 _request_timeout=None):
323        """
324        Makes the HTTP request (synchronous) and return the deserialized data.
325        To make an async request, define a function for callback.
326
327        :param resource_path: Path to method endpoint.
328        :param method: Method to call.
329        :param path_params: Path parameters in the url.
330        :param query_params: Query parameters in the url.
331        :param header_params: Header parameters to be
332            placed in the request header.
333        :param body: Request body.
334        :param post_params dict: Request post form parameters,
335            for `application/x-www-form-urlencoded`, `multipart/form-data`.
336        :param auth_settings list: Auth Settings names for the request.
337        :param response: Response data type.
338        :param files dict: key -> filename, value -> filepath,
339            for `multipart/form-data`.
340        :param callback function: Callback function for asynchronous request.
341            If provide this parameter,
342            the request will be called asynchronously.
343        :param _return_http_data_only: response data without head status code and headers
344        :param collection_formats: dict of collection formats for path, query,
345            header, and post parameters.
346        :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without
347                                 reading/decoding response data. Default is True.
348        :param _request_timeout: timeout setting for this request. If one number provided, it will be total request
349                                 timeout. It can also be a pair (tuple) of (connection, read) timeouts.
350        :return:
351            If provide parameter callback,
352            the request will be called asynchronously.
353            The method will return the request thread.
354            If parameter callback is None,
355            then the method will return the response directly.
356        """
357        if callback is None:
358            return self.__call_api(resource_path, method,
359                                   path_params, query_params, header_params,
360                                   body, post_params, files,
361                                   response_type, auth_settings, callback,
362                                   _return_http_data_only, collection_formats, _preload_content, _request_timeout)
363        else:
364            thread = threading.Thread(target=self.__call_api,
365                                      args=(resource_path, method,
366                                            path_params, query_params,
367                                            header_params, body,
368                                            post_params, files,
369                                            response_type, auth_settings,
370                                            callback, _return_http_data_only,
371                                            collection_formats, _preload_content, _request_timeout))
372        thread.start()
373        return thread
374
375    def request(self, method, url, query_params=None, headers=None,
376                post_params=None, body=None, _preload_content=True, _request_timeout=None):
377        """
378        Makes the HTTP request using RESTClient.
379        """
380        if method == "GET":
381            return self.rest_client.GET(url,
382                                        query_params=query_params,
383                                        _preload_content=_preload_content,
384                                        _request_timeout=_request_timeout,
385                                        headers=headers)
386        elif method == "HEAD":
387            return self.rest_client.HEAD(url,
388                                         query_params=query_params,
389                                         _preload_content=_preload_content,
390                                         _request_timeout=_request_timeout,
391                                         headers=headers)
392        elif method == "OPTIONS":
393            return self.rest_client.OPTIONS(url,
394                                            query_params=query_params,
395                                            headers=headers,
396                                            post_params=post_params,
397                                            _preload_content=_preload_content,
398                                            _request_timeout=_request_timeout,
399                                            body=body)
400        elif method == "POST":
401            return self.rest_client.POST(url,
402                                         query_params=query_params,
403                                         headers=headers,
404                                         post_params=post_params,
405                                         _preload_content=_preload_content,
406                                         _request_timeout=_request_timeout,
407                                         body=body)
408        elif method == "PUT":
409            return self.rest_client.PUT(url,
410                                        query_params=query_params,
411                                        headers=headers,
412                                        post_params=post_params,
413                                        _preload_content=_preload_content,
414                                        _request_timeout=_request_timeout,
415                                        body=body)
416        elif method == "PATCH":
417            return self.rest_client.PATCH(url,
418                                          query_params=query_params,
419                                          headers=headers,
420                                          post_params=post_params,
421                                          _preload_content=_preload_content,
422                                          _request_timeout=_request_timeout,
423                                          body=body)
424        elif method == "DELETE":
425            return self.rest_client.DELETE(url,
426                                           query_params=query_params,
427                                           headers=headers,
428                                           _preload_content=_preload_content,
429                                           _request_timeout=_request_timeout,
430                                           body=body)
431        else:
432            raise ValueError(
433                "http method must be `GET`, `HEAD`, `OPTIONS`,"
434                " `POST`, `PATCH`, `PUT` or `DELETE`."
435            )
436
437    def parameters_to_tuples(self, params, collection_formats):
438        """
439        Get parameters as list of tuples, formatting collections.
440
441        :param params: Parameters as dict or list of two-tuples
442        :param dict collection_formats: Parameter collection formats
443        :return: Parameters as list of tuples, collections formatted
444        """
445        new_params = []
446        if collection_formats is None:
447            collection_formats = {}
448        for k, v in iteritems(params) if isinstance(params, dict) else params:
449            if k in collection_formats:
450                collection_format = collection_formats[k]
451                if collection_format == 'multi':
452                    new_params.extend((k, value) for value in v)
453                else:
454                    if collection_format == 'ssv':
455                        delimiter = ' '
456                    elif collection_format == 'tsv':
457                        delimiter = '\t'
458                    elif collection_format == 'pipes':
459                        delimiter = '|'
460                    else:  # csv is the default
461                        delimiter = ','
462                    new_params.append(
463                        (k, delimiter.join(str(value) for value in v)))
464            else:
465                new_params.append((k, v))
466        return new_params
467
468    def prepare_post_parameters(self, post_params=None, files=None):
469        """
470        Builds form parameters.
471
472        :param post_params: Normal form parameters.
473        :param files: File parameters.
474        :return: Form parameters with files.
475        """
476        params = []
477
478        if post_params:
479            params = post_params
480
481        if files:
482            for k, v in iteritems(files):
483                if not v:
484                    continue
485                file_names = v if type(v) is list else [v]
486                for n in file_names:
487                    with open(n, 'rb') as f:
488                        filename = os.path.basename(f.name)
489                        filedata = f.read()
490                        mimetype = mimetypes.\
491                            guess_type(filename)[0] or 'application/octet-stream'
492                        params.append(tuple([k, tuple([filename, filedata, mimetype])]))
493
494        return params
495
496    def select_header_accept(self, accepts):
497        """
498        Returns `Accept` based on an array of accepts provided.
499
500        :param accepts: List of headers.
501        :return: Accept (e.g. application/json).
502        """
503        if not accepts:
504            return
505
506        accepts = [x.lower() for x in accepts]
507
508        if 'application/json' in accepts:
509            return 'application/json'
510        else:
511            return ', '.join(accepts)
512
513    def select_header_content_type(self, content_types):
514        """
515        Returns `Content-Type` based on an array of content_types provided.
516
517        :param content_types: List of content-types.
518        :return: Content-Type (e.g. application/json).
519        """
520        if not content_types:
521            return 'application/json'
522
523        content_types = [x.lower() for x in content_types]
524
525        if 'application/json' in content_types or '*/*' in content_types:
526            return 'application/json'
527        else:
528            return content_types[0]
529
530    def update_params_for_auth(self, headers, querys, auth_settings):
531        """
532        Updates header and query params based on authentication setting.
533
534        :param headers: Header parameters dict to be updated.
535        :param querys: Query parameters tuple list to be updated.
536        :param auth_settings: Authentication setting identifiers list.
537        """
538        config = Configuration()
539
540        if not auth_settings:
541            return
542
543        for auth in auth_settings:
544            auth_setting = config.auth_settings().get(auth)
545            if auth_setting:
546                if not auth_setting['value']:
547                    continue
548                elif auth_setting['in'] == 'header':
549                    headers[auth_setting['key']] = auth_setting['value']
550                elif auth_setting['in'] == 'query':
551                    querys.append((auth_setting['key'], auth_setting['value']))
552                else:
553                    raise ValueError(
554                        'Authentication token must be in `query` or `header`'
555                    )
556
557    def __deserialize_primitive(self, data, klass):
558        """
559        Deserializes string to primitive type.
560
561        :param data: str.
562        :param klass: class literal.
563
564        :return: int, long, float, str, bool.
565        """
566        try:
567            return klass(data)
568        except UnicodeEncodeError:
569            return unicode(data)
570        except TypeError:
571            return data
572
573    def __deserialize_object(self, value):
574        """
575        Return a original value.
576
577        :return: object.
578        """
579        return value
580
581    def __deserialize_date(self, string):
582        """
583        Deserializes string to date.
584
585        :param string: str.
586        :return: date.
587        """
588        try:
589            from dateutil.parser import parse
590            return parse(string).date()
591        except ImportError:
592            return string
593        except ValueError:
594            raise ApiException(
595                status=0,
596                reason="Failed to parse `{0}` into a date object".format(string)
597            )
598
599    def __deserialize_datatime(self, string):
600        """
601        Deserializes string to datetime.
602
603        The string should be in iso8601 datetime format.
604
605        :param string: str.
606        :return: datetime.
607        """
608        try:
609            from dateutil.parser import parse
610            return parse(string)
611        except ImportError:
612            return string
613        except ValueError:
614            raise ApiException(
615                status=0,
616                reason=(
617                    "Failed to parse `{0}` into a datetime object"
618                    .format(string)
619                )
620            )
621
622    def __deserialize_model(self, data, klass):
623        """
624        Deserializes list or dict to model.
625
626        :param data: dict, list.
627        :param klass: class literal.
628        :return: model object.
629        """
630        instance = klass()
631
632        if not instance.swagger_types:
633            return data
634
635        for attr, attr_type in iteritems(instance.swagger_types):
636            if data is not None \
637               and instance.attribute_map[attr] in data \
638               and isinstance(data, (list, dict)):
639                value = data[instance.attribute_map[attr]]
640                setattr(instance, attr, self.__deserialize(value, attr_type))
641
642        return instance
643
644    def request_jwt_user_token(self, client_id, user_id, oauth_host_name, private_key_bytes, expires_in,
645                               scopes=(OAuth.SCOPE_SIGNATURE,)):
646        """
647        Request JWT User Token
648        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
649        :param user_id: Docusign user Id to be impersonated
650        :param oauth_host_name: Docusign OAuth host name
651        :param private_key_bytes: the byte contents of the RSA private key
652        :param expires_in: number of seconds remaining before the JWT assertion is considered as invalid
653        :param scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any
654        advanced scope.
655        :return: OAuthToken object
656        """
657        if not private_key_bytes:
658            raise ArgumentException("Private key not supplied or is invalid!")
659        if not user_id:
660            raise ArgumentException("User Id not supplied or is invalid!")
661        if not oauth_host_name:
662            raise ArgumentException("oAuthBasePath cannot be empty")
663
664        now = math.floor(time())
665        later = now + (expires_in * 1)
666        claim = {"iss": client_id, "sub": user_id, "aud": oauth_host_name, "iat": now, "exp": later,
667                 "scope": " ".join(scopes)}
668        token = jwt.encode(payload=claim, key=private_key_bytes, algorithm='RS256')
669        response = self.request("POST", "https://" + oauth_host_name + "/oauth/token",
670                                headers=self.sanitize_for_serialization(
671                                    {"Content-Type": "application/x-www-form-urlencoded"}),
672                                post_params=self.sanitize_for_serialization(
673                                    {"assertion": token, "grant_type": OAuth.GRANT_TYPE_JWT}))
674
675        response_data = json.loads(response.data)
676
677        if 'token_type' in response_data and 'access_token' in response_data:
678            self.set_default_header("Authorization", response_data['token_type'] + " " + response_data['access_token'])
679        else:
680            raise ApiException(status=response.status,
681                               reason="Error while requesting server, received a non successful HTTP code {}"
682                                       " with response Body: {}".format(response.status, response.data)
683                               )
684
685        return self.deserialize(response=response, response_type=OAuthToken)
686
687    def request_jwt_application_token(self, client_id, oauth_host_name, private_key_bytes, expires_in,
688                                      scopes=(OAuth.SCOPE_SIGNATURE,)):
689        """
690        Request JWT Application Token
691        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
692        :param oauth_host_name: Docusign OAuth host name
693        :param private_key_bytes: the byte contents of the RSA private key
694        :param expires_in: number of seconds remaining before the JWT assertion is considered as invalid
695        :param scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any
696        advanced scope.
697        :return: OAuthToken object
698        """
699
700        if not private_key_bytes:
701            raise ArgumentException("Private key not supplied or is invalid!")
702        if not oauth_host_name:
703            raise ArgumentException("oAuthBasePath cannot be empty")
704
705        now = math.floor(time())
706        later = now + (expires_in * 1)
707        claim = {"iss": client_id, "aud": oauth_host_name, "iat": now, "exp": later,
708                 "scope": " ".join(scopes)}
709        token = jwt.encode(payload=claim, key=private_key_bytes, algorithm='RS256')
710
711        response = self.request("POST", "https://" + oauth_host_name + "/oauth/token",
712                                headers=self.sanitize_for_serialization(
713                                    {"Content-Type": "application/x-www-form-urlencoded"}),
714                                post_params=self.sanitize_for_serialization(
715                                    {"assertion": token, "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer"}))
716        response_data = json.loads(response.data)
717
718        if 'token_type' in response_data and 'access_token' in response_data:
719            self.set_default_header("Authorization", response_data['token_type'] + " " + response_data['access_token'])
720        else:
721            ApiException(status=response.status,
722                         reason="Error while requesting server, received a non successful HTTP code {}"
723                                " with response Body: {}".format(response.status, response.data)
724                         )
725
726        return self.deserialize(response=response, response_type=OAuthToken)
727
728    def get_user_info(self, access_token):
729        """
730        Get User Info method takes the accessToken to retrieve User Account Data.
731        :param access_token:
732        :return: The User Info model.
733        """
734        if not access_token:
735            raise ArgumentException("Cannot find a valid access token."
736                                    " Make sure OAuth is configured before you try again.")
737        if not self.oauth_host_name:
738            raise ArgumentException("oAuthBasePath cannot be empty")
739
740        resource_path = '/oauth/userinfo'
741        headers = {"Authorization": "Bearer " + access_token}
742
743        response = self.request("GET", "https://" + self.oauth_host_name + resource_path, headers=headers)
744        return self.deserialize(response=response, response_type=OAuthUserInfo)
745
746    def generate_access_token(self, client_id, client_secret, code):
747        """
748        GenerateAccessToken will exchange the authorization code for an access token and refresh tokens.
749        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
750        :param client_secret: The secret key you generated when you set up the integration in Docusign Admin console.
751        :param code: The authorization code
752        :return: OAuthToken object
753        """
754        if not client_id or not client_secret or not code:
755            raise ArgumentException
756        url = "https://{0}/oauth/token".format(self.oauth_host_name)
757        integrator_and_secret_key = b"Basic " + base64.b64encode(str.encode("{}:{}".format(client_id, client_secret)))
758        headers = {
759            "Authorization": integrator_and_secret_key.decode("utf-8"),
760            "Content-Type": "application/x-www-form-urlencoded",
761        }
762        post_params = self.sanitize_for_serialization({
763            "grant_type": "authorization_code",
764            "code": code
765        })
766        response = self.rest_client.POST(url, headers=headers, post_params=post_params)
767
768        return self.deserialize(response=response, response_type=OAuthToken)
769
770    def set_base_path(self, base_path):
771        """
772        :param base_path:
773        :return:
774        """
775        self.base_path = base_path
776
777    def set_oauth_host_name(self, oauth_host_name=None):
778        """
779        :param oauth_host_name:
780        :return:
781        """
782        if oauth_host_name:
783            self.oauth_host_name = oauth_host_name
784            return
785
786        # Derive OAuth Base Path if not given
787        if self.base_path is None or self.base_path.startswith("https://demo") or self.base_path.startswith("http://demo") or self.base_path.startswith("https://apps-d") or self.base_path.startswith("http://apps-d"):
788            self.oauth_host_name = OAuth.DEMO_OAUTH_BASE_PATH
789        elif self.base_path.startswith("https://stage") or self.base_path.startswith("http://stage") or self.base_path.startswith("https://apps-s") or self.base_path.startswith("http://apps-s"):
790            self.oauth_host_name = OAuth.STAGE_OAUTH_BASE_PATH
791        else:
792            self.oauth_host_name = OAuth.PRODUCTION_OAUTH_BASE_PATH
793
794    def set_access_token(self, token_obj):
795        """
796
797        :param token_obj: 
798        :return: 
799        """
800        self.default_headers['Authorization'] = token_obj.access_token
801
802    def get_authorization_uri(self, client_id, scopes, redirect_uri, response_type, state=None):
803        """
804        Helper method to configure the OAuth accessCode/implicit flow parameters
805        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
806        :param scopes: The list of requested scopes.  Client applications may be scoped to a limited set of system access.
807        :param redirect_uri: This determines where to deliver the response containing the authorization code
808        :param response_type: Determines the response type of the authorization request, NOTE: these response types are
809        mutually exclusive for a client application. A public/native client application may only request a response type
810         of "token". A private/trusted client application may only request a response type of "code".
811        :param state: Allows for arbitrary state that may be useful to your application. The value in this parameter
812        will be round-tripped along with the response so you can make sure it didn't change.
813        :return: string
814        """
815        if not self.oauth_host_name:
816            self.oauth_host_name = self.get_oauth_host_name()
817        scopes = " ".join(scopes) if scopes else ""
818        uri = "https://{}/oauth/auth?response_type={}&scope={}&client_id={}&redirect_uri={}"
819        if state:
820            uri += "&state={}"
821        return uri.format(self.oauth_host_name, response_type, quote(scopes), client_id, redirect_uri, state)
822
823    def get_oauth_host_name(self):
824        """
825        :return: string
826        """
827        if not self.oauth_host_name:
828            self.set_oauth_host_name()
829
830        return self.oauth_host_name

Generic API client for Swagger client library builds.

Swagger generic API client. This client handles the client- server communication, and is invariant across implementations. Specifics of the methods and models for each application are generated from the Swagger templates.

NOTE: This class is auto generated by the swagger code generator program. Ref: https://github.com/swagger-api/swagger-codegen Do not edit the class manually.

Parameters
  • host: The base path for the server to call.
  • header_name: a header to pass when making calls to the API.
  • header_value: a header value to pass when making calls to the API.
ApiClient( host=None, header_name=None, header_value=None, cookie=None, oauth_host_name=None, base_path=None)
73    def __init__(self, host=None, header_name=None, header_value=None, cookie=None, oauth_host_name=None, base_path=None):
74        """
75        Constructor of the class.
76        """
77
78        config = Configuration()
79        self.rest_client = RESTClientObject(configuration=config)
80        self.default_headers = {'X-DocuSign-SDK': 'Python'}
81        if header_name is not None:
82            self.default_headers[header_name] = header_value
83        if host is None:
84            self.host = config.host
85        elif host == "":
86            raise ArgumentException("basePath cannot be empty")
87        else:
88            self.host = host
89
90        self.cookie = cookie
91        self.base_path = base_path
92        self.set_oauth_host_name(oauth_host_name)
93
94        # Set default User-Agent.
95        self.user_agent = config.user_agent

Constructor of the class.

PRIMITIVE_TYPES = (<class 'float'>, <class 'bool'>, <class 'bytes'>, <class 'str'>, <class 'int'>)
NATIVE_TYPES_MAPPING = {'int': <class 'int'>, 'long': <class 'int'>, 'float': <class 'float'>, 'str': <class 'str'>, 'bool': <class 'bool'>, 'date': <class 'datetime.date'>, 'datetime': <class 'datetime.datetime'>, 'object': <class 'object'>}
OAUTH_TYPES = ('OAuthToken', 'OAuthUserInfo', 'Account', 'Organization', 'Link')
user_agent

Gets user agent.

def set_default_header(self, header_name, header_value)
111    def set_default_header(self, header_name, header_value):
112        self.default_headers[header_name] = header_value
def sanitize_for_serialization(self, obj)
209    def sanitize_for_serialization(self, obj):
210        """
211        Builds a JSON POST object.
212
213        If obj is None, return None.
214        If obj is str, int, long, float, bool, return directly.
215        If obj is datetime.datetime, datetime.date
216            convert to string in iso8601 format.
217        If obj is list, sanitize each element in the list.
218        If obj is dict, return the dict.
219        If obj is swagger model, return the properties dict.
220
221        :param obj: The data to serialize.
222        :return: The serialized form of data.
223        """
224        if obj is None:
225            return None
226        elif isinstance(obj, self.PRIMITIVE_TYPES):
227            return obj
228        elif isinstance(obj, list):
229            return [self.sanitize_for_serialization(sub_obj)
230                    for sub_obj in obj]
231        elif isinstance(obj, tuple):
232            return tuple(self.sanitize_for_serialization(sub_obj)
233                         for sub_obj in obj)
234        elif isinstance(obj, (datetime, date)):
235            return obj.isoformat()
236
237        if isinstance(obj, dict):
238            obj_dict = obj
239        else:
240            # Convert model obj to dict except
241            # attributes `swagger_types`, `attribute_map`
242            # and attributes which value is not None.
243            # Convert attribute name to json key in
244            # model definition for request.
245            obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
246                        for attr, _ in iteritems(obj.swagger_types)
247                        if getattr(obj, attr) is not None}
248
249        return {key: self.sanitize_for_serialization(val)
250                for key, val in iteritems(obj_dict)}

Builds a JSON POST object.

If obj is None, return None. If obj is str, int, long, float, bool, return directly. If obj is datetime.datetime, datetime.date convert to string in iso8601 format. If obj is list, sanitize each element in the list. If obj is dict, return the dict. If obj is swagger model, return the properties dict.

Parameters
  • obj: The data to serialize.
Returns

The serialized form of data.

def deserialize(self, response, response_type)
252    def deserialize(self, response, response_type):
253        """
254        Deserializes response into an object.
255
256        :param response: RESTResponse object to be deserialized.
257        :param response_type: class literal for
258            deserialized object, or string of class name.
259
260        :return: deserialized object.
261        """
262        # handle file type response
263        if response_type == "file":
264            return response.data
265
266        # fetch data from response object
267        try:
268            data = json.loads(response.data)
269        except ValueError:
270            data = response.data
271
272        return self.__deserialize(data, response_type)

Deserializes response into an object.

Parameters
  • response: RESTResponse object to be deserialized.
  • response_type: class literal for deserialized object, or string of class name.
Returns

deserialized object.

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)
317    def call_api(self, resource_path, method,
318                 path_params=None, query_params=None, header_params=None,
319                 body=None, post_params=None, files=None,
320                 response_type=None, auth_settings=None, callback=None,
321                 _return_http_data_only=None, collection_formats=None, _preload_content=True,
322                 _request_timeout=None):
323        """
324        Makes the HTTP request (synchronous) and return the deserialized data.
325        To make an async request, define a function for callback.
326
327        :param resource_path: Path to method endpoint.
328        :param method: Method to call.
329        :param path_params: Path parameters in the url.
330        :param query_params: Query parameters in the url.
331        :param header_params: Header parameters to be
332            placed in the request header.
333        :param body: Request body.
334        :param post_params dict: Request post form parameters,
335            for `application/x-www-form-urlencoded`, `multipart/form-data`.
336        :param auth_settings list: Auth Settings names for the request.
337        :param response: Response data type.
338        :param files dict: key -> filename, value -> filepath,
339            for `multipart/form-data`.
340        :param callback function: Callback function for asynchronous request.
341            If provide this parameter,
342            the request will be called asynchronously.
343        :param _return_http_data_only: response data without head status code and headers
344        :param collection_formats: dict of collection formats for path, query,
345            header, and post parameters.
346        :param _preload_content: if False, the urllib3.HTTPResponse object will be returned without
347                                 reading/decoding response data. Default is True.
348        :param _request_timeout: timeout setting for this request. If one number provided, it will be total request
349                                 timeout. It can also be a pair (tuple) of (connection, read) timeouts.
350        :return:
351            If provide parameter callback,
352            the request will be called asynchronously.
353            The method will return the request thread.
354            If parameter callback is None,
355            then the method will return the response directly.
356        """
357        if callback is None:
358            return self.__call_api(resource_path, method,
359                                   path_params, query_params, header_params,
360                                   body, post_params, files,
361                                   response_type, auth_settings, callback,
362                                   _return_http_data_only, collection_formats, _preload_content, _request_timeout)
363        else:
364            thread = threading.Thread(target=self.__call_api,
365                                      args=(resource_path, method,
366                                            path_params, query_params,
367                                            header_params, body,
368                                            post_params, files,
369                                            response_type, auth_settings,
370                                            callback, _return_http_data_only,
371                                            collection_formats, _preload_content, _request_timeout))
372        thread.start()
373        return thread

Makes the HTTP request (synchronous) and return the deserialized data. To make an async request, define a function for callback.

Parameters
  • resource_path: Path to method endpoint.
  • method: Method to call.
  • path_params: Path parameters in the url.
  • query_params: Query parameters in the url.
  • header_params: Header parameters to be placed in the request header.
  • body: Request body.
  • post_params dict: Request post form parameters, for application/x-www-form-urlencoded, multipart/form-data.
  • auth_settings list: Auth Settings names for the request.
  • response: Response data type.
  • files dict: key -> filename, value -> filepath, for multipart/form-data.
  • callback function: Callback function for asynchronous request. If provide this parameter, the request will be called asynchronously.
  • _return_http_data_only: response data without head status code and headers
  • collection_formats: dict of collection formats for path, query, header, and post parameters.
  • _preload_content: if False, the urllib3.HTTPResponse object will be returned without reading/decoding response data. Default is True.
  • _request_timeout: timeout setting for this request. If one number provided, it will be total request timeout. It can also be a pair (tuple) of (connection, read) timeouts.
Returns
If provide parameter callback,
the request will be called asynchronously.
The method will return the request thread.
If parameter callback is None,
then the method will return the response directly.
def request( self, method, url, query_params=None, headers=None, post_params=None, body=None, _preload_content=True, _request_timeout=None)
375    def request(self, method, url, query_params=None, headers=None,
376                post_params=None, body=None, _preload_content=True, _request_timeout=None):
377        """
378        Makes the HTTP request using RESTClient.
379        """
380        if method == "GET":
381            return self.rest_client.GET(url,
382                                        query_params=query_params,
383                                        _preload_content=_preload_content,
384                                        _request_timeout=_request_timeout,
385                                        headers=headers)
386        elif method == "HEAD":
387            return self.rest_client.HEAD(url,
388                                         query_params=query_params,
389                                         _preload_content=_preload_content,
390                                         _request_timeout=_request_timeout,
391                                         headers=headers)
392        elif method == "OPTIONS":
393            return self.rest_client.OPTIONS(url,
394                                            query_params=query_params,
395                                            headers=headers,
396                                            post_params=post_params,
397                                            _preload_content=_preload_content,
398                                            _request_timeout=_request_timeout,
399                                            body=body)
400        elif method == "POST":
401            return self.rest_client.POST(url,
402                                         query_params=query_params,
403                                         headers=headers,
404                                         post_params=post_params,
405                                         _preload_content=_preload_content,
406                                         _request_timeout=_request_timeout,
407                                         body=body)
408        elif method == "PUT":
409            return self.rest_client.PUT(url,
410                                        query_params=query_params,
411                                        headers=headers,
412                                        post_params=post_params,
413                                        _preload_content=_preload_content,
414                                        _request_timeout=_request_timeout,
415                                        body=body)
416        elif method == "PATCH":
417            return self.rest_client.PATCH(url,
418                                          query_params=query_params,
419                                          headers=headers,
420                                          post_params=post_params,
421                                          _preload_content=_preload_content,
422                                          _request_timeout=_request_timeout,
423                                          body=body)
424        elif method == "DELETE":
425            return self.rest_client.DELETE(url,
426                                           query_params=query_params,
427                                           headers=headers,
428                                           _preload_content=_preload_content,
429                                           _request_timeout=_request_timeout,
430                                           body=body)
431        else:
432            raise ValueError(
433                "http method must be `GET`, `HEAD`, `OPTIONS`,"
434                " `POST`, `PATCH`, `PUT` or `DELETE`."
435            )

Makes the HTTP request using RESTClient.

def parameters_to_tuples(self, params, collection_formats)
437    def parameters_to_tuples(self, params, collection_formats):
438        """
439        Get parameters as list of tuples, formatting collections.
440
441        :param params: Parameters as dict or list of two-tuples
442        :param dict collection_formats: Parameter collection formats
443        :return: Parameters as list of tuples, collections formatted
444        """
445        new_params = []
446        if collection_formats is None:
447            collection_formats = {}
448        for k, v in iteritems(params) if isinstance(params, dict) else params:
449            if k in collection_formats:
450                collection_format = collection_formats[k]
451                if collection_format == 'multi':
452                    new_params.extend((k, value) for value in v)
453                else:
454                    if collection_format == 'ssv':
455                        delimiter = ' '
456                    elif collection_format == 'tsv':
457                        delimiter = '\t'
458                    elif collection_format == 'pipes':
459                        delimiter = '|'
460                    else:  # csv is the default
461                        delimiter = ','
462                    new_params.append(
463                        (k, delimiter.join(str(value) for value in v)))
464            else:
465                new_params.append((k, v))
466        return new_params

Get parameters as list of tuples, formatting collections.

Parameters
  • params: Parameters as dict or list of two-tuples
  • dict collection_formats: Parameter collection formats
Returns

Parameters as list of tuples, collections formatted

def prepare_post_parameters(self, post_params=None, files=None)
468    def prepare_post_parameters(self, post_params=None, files=None):
469        """
470        Builds form parameters.
471
472        :param post_params: Normal form parameters.
473        :param files: File parameters.
474        :return: Form parameters with files.
475        """
476        params = []
477
478        if post_params:
479            params = post_params
480
481        if files:
482            for k, v in iteritems(files):
483                if not v:
484                    continue
485                file_names = v if type(v) is list else [v]
486                for n in file_names:
487                    with open(n, 'rb') as f:
488                        filename = os.path.basename(f.name)
489                        filedata = f.read()
490                        mimetype = mimetypes.\
491                            guess_type(filename)[0] or 'application/octet-stream'
492                        params.append(tuple([k, tuple([filename, filedata, mimetype])]))
493
494        return params

Builds form parameters.

Parameters
  • post_params: Normal form parameters.
  • files: File parameters.
Returns

Form parameters with files.

def select_header_accept(self, accepts)
496    def select_header_accept(self, accepts):
497        """
498        Returns `Accept` based on an array of accepts provided.
499
500        :param accepts: List of headers.
501        :return: Accept (e.g. application/json).
502        """
503        if not accepts:
504            return
505
506        accepts = [x.lower() for x in accepts]
507
508        if 'application/json' in accepts:
509            return 'application/json'
510        else:
511            return ', '.join(accepts)

Returns Accept based on an array of accepts provided.

Parameters
  • accepts: List of headers.
Returns

Accept (e.g. application/json).

def select_header_content_type(self, content_types)
513    def select_header_content_type(self, content_types):
514        """
515        Returns `Content-Type` based on an array of content_types provided.
516
517        :param content_types: List of content-types.
518        :return: Content-Type (e.g. application/json).
519        """
520        if not content_types:
521            return 'application/json'
522
523        content_types = [x.lower() for x in content_types]
524
525        if 'application/json' in content_types or '*/*' in content_types:
526            return 'application/json'
527        else:
528            return content_types[0]

Returns Content-Type based on an array of content_types provided.

Parameters
  • content_types: List of content-types.
Returns

Content-Type (e.g. application/json).

def update_params_for_auth(self, headers, querys, auth_settings)
530    def update_params_for_auth(self, headers, querys, auth_settings):
531        """
532        Updates header and query params based on authentication setting.
533
534        :param headers: Header parameters dict to be updated.
535        :param querys: Query parameters tuple list to be updated.
536        :param auth_settings: Authentication setting identifiers list.
537        """
538        config = Configuration()
539
540        if not auth_settings:
541            return
542
543        for auth in auth_settings:
544            auth_setting = config.auth_settings().get(auth)
545            if auth_setting:
546                if not auth_setting['value']:
547                    continue
548                elif auth_setting['in'] == 'header':
549                    headers[auth_setting['key']] = auth_setting['value']
550                elif auth_setting['in'] == 'query':
551                    querys.append((auth_setting['key'], auth_setting['value']))
552                else:
553                    raise ValueError(
554                        'Authentication token must be in `query` or `header`'
555                    )

Updates header and query params based on authentication setting.

Parameters
  • headers: Header parameters dict to be updated.
  • querys: Query parameters tuple list to be updated.
  • auth_settings: Authentication setting identifiers list.
def request_jwt_user_token( self, client_id, user_id, oauth_host_name, private_key_bytes, expires_in, scopes=('signature',))
644    def request_jwt_user_token(self, client_id, user_id, oauth_host_name, private_key_bytes, expires_in,
645                               scopes=(OAuth.SCOPE_SIGNATURE,)):
646        """
647        Request JWT User Token
648        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
649        :param user_id: Docusign user Id to be impersonated
650        :param oauth_host_name: Docusign OAuth host name
651        :param private_key_bytes: the byte contents of the RSA private key
652        :param expires_in: number of seconds remaining before the JWT assertion is considered as invalid
653        :param scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any
654        advanced scope.
655        :return: OAuthToken object
656        """
657        if not private_key_bytes:
658            raise ArgumentException("Private key not supplied or is invalid!")
659        if not user_id:
660            raise ArgumentException("User Id not supplied or is invalid!")
661        if not oauth_host_name:
662            raise ArgumentException("oAuthBasePath cannot be empty")
663
664        now = math.floor(time())
665        later = now + (expires_in * 1)
666        claim = {"iss": client_id, "sub": user_id, "aud": oauth_host_name, "iat": now, "exp": later,
667                 "scope": " ".join(scopes)}
668        token = jwt.encode(payload=claim, key=private_key_bytes, algorithm='RS256')
669        response = self.request("POST", "https://" + oauth_host_name + "/oauth/token",
670                                headers=self.sanitize_for_serialization(
671                                    {"Content-Type": "application/x-www-form-urlencoded"}),
672                                post_params=self.sanitize_for_serialization(
673                                    {"assertion": token, "grant_type": OAuth.GRANT_TYPE_JWT}))
674
675        response_data = json.loads(response.data)
676
677        if 'token_type' in response_data and 'access_token' in response_data:
678            self.set_default_header("Authorization", response_data['token_type'] + " " + response_data['access_token'])
679        else:
680            raise ApiException(status=response.status,
681                               reason="Error while requesting server, received a non successful HTTP code {}"
682                                       " with response Body: {}".format(response.status, response.data)
683                               )
684
685        return self.deserialize(response=response, response_type=OAuthToken)

Request JWT User Token

Parameters
  • client_id: Docusign OAuth Client Id(AKA Integrator Key)
  • user_id: Docusign user Id to be impersonated
  • oauth_host_name: Docusign OAuth host name
  • private_key_bytes: the byte contents of the RSA private key
  • expires_in: number of seconds remaining before the JWT assertion is considered as invalid
  • scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any advanced scope.
Returns

OAuthToken object

def request_jwt_application_token( self, client_id, oauth_host_name, private_key_bytes, expires_in, scopes=('signature',))
687    def request_jwt_application_token(self, client_id, oauth_host_name, private_key_bytes, expires_in,
688                                      scopes=(OAuth.SCOPE_SIGNATURE,)):
689        """
690        Request JWT Application Token
691        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
692        :param oauth_host_name: Docusign OAuth host name
693        :param private_key_bytes: the byte contents of the RSA private key
694        :param expires_in: number of seconds remaining before the JWT assertion is considered as invalid
695        :param scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any
696        advanced scope.
697        :return: OAuthToken object
698        """
699
700        if not private_key_bytes:
701            raise ArgumentException("Private key not supplied or is invalid!")
702        if not oauth_host_name:
703            raise ArgumentException("oAuthBasePath cannot be empty")
704
705        now = math.floor(time())
706        later = now + (expires_in * 1)
707        claim = {"iss": client_id, "aud": oauth_host_name, "iat": now, "exp": later,
708                 "scope": " ".join(scopes)}
709        token = jwt.encode(payload=claim, key=private_key_bytes, algorithm='RS256')
710
711        response = self.request("POST", "https://" + oauth_host_name + "/oauth/token",
712                                headers=self.sanitize_for_serialization(
713                                    {"Content-Type": "application/x-www-form-urlencoded"}),
714                                post_params=self.sanitize_for_serialization(
715                                    {"assertion": token, "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer"}))
716        response_data = json.loads(response.data)
717
718        if 'token_type' in response_data and 'access_token' in response_data:
719            self.set_default_header("Authorization", response_data['token_type'] + " " + response_data['access_token'])
720        else:
721            ApiException(status=response.status,
722                         reason="Error while requesting server, received a non successful HTTP code {}"
723                                " with response Body: {}".format(response.status, response.data)
724                         )
725
726        return self.deserialize(response=response, response_type=OAuthToken)

Request JWT Application Token

Parameters
  • client_id: Docusign OAuth Client Id(AKA Integrator Key)
  • oauth_host_name: Docusign OAuth host name
  • private_key_bytes: the byte contents of the RSA private key
  • expires_in: number of seconds remaining before the JWT assertion is considered as invalid
  • scopes: Optional. The list of requested scopes may include (but not limited to) You can also pass any advanced scope.
Returns

OAuthToken object

def get_user_info(self, access_token)
728    def get_user_info(self, access_token):
729        """
730        Get User Info method takes the accessToken to retrieve User Account Data.
731        :param access_token:
732        :return: The User Info model.
733        """
734        if not access_token:
735            raise ArgumentException("Cannot find a valid access token."
736                                    " Make sure OAuth is configured before you try again.")
737        if not self.oauth_host_name:
738            raise ArgumentException("oAuthBasePath cannot be empty")
739
740        resource_path = '/oauth/userinfo'
741        headers = {"Authorization": "Bearer " + access_token}
742
743        response = self.request("GET", "https://" + self.oauth_host_name + resource_path, headers=headers)
744        return self.deserialize(response=response, response_type=OAuthUserInfo)

Get User Info method takes the accessToken to retrieve User Account Data.

Parameters
  • access_token:
Returns

The User Info model.

def generate_access_token(self, client_id, client_secret, code)
746    def generate_access_token(self, client_id, client_secret, code):
747        """
748        GenerateAccessToken will exchange the authorization code for an access token and refresh tokens.
749        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
750        :param client_secret: The secret key you generated when you set up the integration in Docusign Admin console.
751        :param code: The authorization code
752        :return: OAuthToken object
753        """
754        if not client_id or not client_secret or not code:
755            raise ArgumentException
756        url = "https://{0}/oauth/token".format(self.oauth_host_name)
757        integrator_and_secret_key = b"Basic " + base64.b64encode(str.encode("{}:{}".format(client_id, client_secret)))
758        headers = {
759            "Authorization": integrator_and_secret_key.decode("utf-8"),
760            "Content-Type": "application/x-www-form-urlencoded",
761        }
762        post_params = self.sanitize_for_serialization({
763            "grant_type": "authorization_code",
764            "code": code
765        })
766        response = self.rest_client.POST(url, headers=headers, post_params=post_params)
767
768        return self.deserialize(response=response, response_type=OAuthToken)

GenerateAccessToken will exchange the authorization code for an access token and refresh tokens.

Parameters
  • client_id: Docusign OAuth Client Id(AKA Integrator Key)
  • client_secret: The secret key you generated when you set up the integration in Docusign Admin console.
  • code: The authorization code
Returns

OAuthToken object

def set_base_path(self, base_path)
770    def set_base_path(self, base_path):
771        """
772        :param base_path:
773        :return:
774        """
775        self.base_path = base_path
Parameters
  • base_path:
Returns
def set_oauth_host_name(self, oauth_host_name=None)
777    def set_oauth_host_name(self, oauth_host_name=None):
778        """
779        :param oauth_host_name:
780        :return:
781        """
782        if oauth_host_name:
783            self.oauth_host_name = oauth_host_name
784            return
785
786        # Derive OAuth Base Path if not given
787        if self.base_path is None or self.base_path.startswith("https://demo") or self.base_path.startswith("http://demo") or self.base_path.startswith("https://apps-d") or self.base_path.startswith("http://apps-d"):
788            self.oauth_host_name = OAuth.DEMO_OAUTH_BASE_PATH
789        elif self.base_path.startswith("https://stage") or self.base_path.startswith("http://stage") or self.base_path.startswith("https://apps-s") or self.base_path.startswith("http://apps-s"):
790            self.oauth_host_name = OAuth.STAGE_OAUTH_BASE_PATH
791        else:
792            self.oauth_host_name = OAuth.PRODUCTION_OAUTH_BASE_PATH
Parameters
  • oauth_host_name:
Returns
def set_access_token(self, token_obj)
794    def set_access_token(self, token_obj):
795        """
796
797        :param token_obj: 
798        :return: 
799        """
800        self.default_headers['Authorization'] = token_obj.access_token
Parameters
  • token_obj:
Returns
def get_authorization_uri(self, client_id, scopes, redirect_uri, response_type, state=None)
802    def get_authorization_uri(self, client_id, scopes, redirect_uri, response_type, state=None):
803        """
804        Helper method to configure the OAuth accessCode/implicit flow parameters
805        :param client_id: Docusign OAuth Client Id(AKA Integrator Key)
806        :param scopes: The list of requested scopes.  Client applications may be scoped to a limited set of system access.
807        :param redirect_uri: This determines where to deliver the response containing the authorization code
808        :param response_type: Determines the response type of the authorization request, NOTE: these response types are
809        mutually exclusive for a client application. A public/native client application may only request a response type
810         of "token". A private/trusted client application may only request a response type of "code".
811        :param state: Allows for arbitrary state that may be useful to your application. The value in this parameter
812        will be round-tripped along with the response so you can make sure it didn't change.
813        :return: string
814        """
815        if not self.oauth_host_name:
816            self.oauth_host_name = self.get_oauth_host_name()
817        scopes = " ".join(scopes) if scopes else ""
818        uri = "https://{}/oauth/auth?response_type={}&scope={}&client_id={}&redirect_uri={}"
819        if state:
820            uri += "&state={}"
821        return uri.format(self.oauth_host_name, response_type, quote(scopes), client_id, redirect_uri, state)

Helper method to configure the OAuth accessCode/implicit flow parameters

Parameters
  • client_id: Docusign OAuth Client Id(AKA Integrator Key)
  • scopes: The list of requested scopes. Client applications may be scoped to a limited set of system access.
  • redirect_uri: This determines where to deliver the response containing the authorization code
  • response_type: Determines the response type of the authorization request, NOTE: these response types are mutually exclusive for a client application. A public/native client application may only request a response type of "token". A private/trusted client application may only request a response type of "code".
  • state: Allows for arbitrary state that may be useful to your application. The value in this parameter will be round-tripped along with the response so you can make sure it didn't change.
Returns

string

def get_oauth_host_name(self)
823    def get_oauth_host_name(self):
824        """
825        :return: string
826        """
827        if not self.oauth_host_name:
828            self.set_oauth_host_name()
829
830        return self.oauth_host_name
Returns

string