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