amazon-creatorsapi-python-sdk 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. amazon_creatorsapi_python_sdk-1.0.0.dist-info/METADATA +99 -0
  2. amazon_creatorsapi_python_sdk-1.0.0.dist-info/RECORD +108 -0
  3. amazon_creatorsapi_python_sdk-1.0.0.dist-info/WHEEL +5 -0
  4. amazon_creatorsapi_python_sdk-1.0.0.dist-info/licenses/LICENSE.txt +202 -0
  5. amazon_creatorsapi_python_sdk-1.0.0.dist-info/licenses/NOTICE.txt +2 -0
  6. amazon_creatorsapi_python_sdk-1.0.0.dist-info/top_level.txt +1 -0
  7. creatorsapi_python_sdk/__init__.py +127 -0
  8. creatorsapi_python_sdk/api/__init__.py +5 -0
  9. creatorsapi_python_sdk/api/default_api.py +2412 -0
  10. creatorsapi_python_sdk/api_client.py +912 -0
  11. creatorsapi_python_sdk/api_response.py +21 -0
  12. creatorsapi_python_sdk/auth/__init__.py +21 -0
  13. creatorsapi_python_sdk/auth/oauth2_config.py +123 -0
  14. creatorsapi_python_sdk/auth/oauth2_token_manager.py +120 -0
  15. creatorsapi_python_sdk/configuration.py +455 -0
  16. creatorsapi_python_sdk/exceptions.py +204 -0
  17. creatorsapi_python_sdk/models/__init__.py +110 -0
  18. creatorsapi_python_sdk/models/access_denied_exception_response_content.py +97 -0
  19. creatorsapi_python_sdk/models/access_denied_reason.py +45 -0
  20. creatorsapi_python_sdk/models/availability.py +44 -0
  21. creatorsapi_python_sdk/models/browse_node.py +116 -0
  22. creatorsapi_python_sdk/models/browse_node_ancestor.py +103 -0
  23. creatorsapi_python_sdk/models/browse_node_child.py +96 -0
  24. creatorsapi_python_sdk/models/browse_node_info.py +106 -0
  25. creatorsapi_python_sdk/models/browse_nodes_result.py +100 -0
  26. creatorsapi_python_sdk/models/by_line_info.py +111 -0
  27. creatorsapi_python_sdk/models/classifications.py +101 -0
  28. creatorsapi_python_sdk/models/condition.py +44 -0
  29. creatorsapi_python_sdk/models/content_info.py +113 -0
  30. creatorsapi_python_sdk/models/content_rating.py +96 -0
  31. creatorsapi_python_sdk/models/contributor.py +98 -0
  32. creatorsapi_python_sdk/models/customer_reviews.py +98 -0
  33. creatorsapi_python_sdk/models/deal_details.py +102 -0
  34. creatorsapi_python_sdk/models/delivery_flag.py +46 -0
  35. creatorsapi_python_sdk/models/dimension_based_attribute.py +111 -0
  36. creatorsapi_python_sdk/models/error_data.py +94 -0
  37. creatorsapi_python_sdk/models/external_ids.py +106 -0
  38. creatorsapi_python_sdk/models/feed.py +98 -0
  39. creatorsapi_python_sdk/models/get_browse_nodes_request_content.py +107 -0
  40. creatorsapi_python_sdk/models/get_browse_nodes_resource.py +44 -0
  41. creatorsapi_python_sdk/models/get_browse_nodes_response_content.py +106 -0
  42. creatorsapi_python_sdk/models/get_feed_request_content.py +93 -0
  43. creatorsapi_python_sdk/models/get_feed_response_content.py +92 -0
  44. creatorsapi_python_sdk/models/get_items_request_content.py +124 -0
  45. creatorsapi_python_sdk/models/get_items_resource.py +76 -0
  46. creatorsapi_python_sdk/models/get_items_response_content.py +106 -0
  47. creatorsapi_python_sdk/models/get_report_request_content.py +93 -0
  48. creatorsapi_python_sdk/models/get_report_response_content.py +92 -0
  49. creatorsapi_python_sdk/models/get_variations_request_content.py +135 -0
  50. creatorsapi_python_sdk/models/get_variations_resource.py +79 -0
  51. creatorsapi_python_sdk/models/get_variations_response_content.py +106 -0
  52. creatorsapi_python_sdk/models/image_size.py +96 -0
  53. creatorsapi_python_sdk/models/image_type.py +111 -0
  54. creatorsapi_python_sdk/models/images.py +105 -0
  55. creatorsapi_python_sdk/models/internal_server_exception_response_content.py +94 -0
  56. creatorsapi_python_sdk/models/item.py +138 -0
  57. creatorsapi_python_sdk/models/item_info.py +156 -0
  58. creatorsapi_python_sdk/models/items_result.py +100 -0
  59. creatorsapi_python_sdk/models/language_type.py +94 -0
  60. creatorsapi_python_sdk/models/languages.py +104 -0
  61. creatorsapi_python_sdk/models/list_feeds_response_content.py +100 -0
  62. creatorsapi_python_sdk/models/list_reports_response_content.py +100 -0
  63. creatorsapi_python_sdk/models/manufacture_info.py +106 -0
  64. creatorsapi_python_sdk/models/money.py +96 -0
  65. creatorsapi_python_sdk/models/multi_valued_attribute.py +96 -0
  66. creatorsapi_python_sdk/models/offer_availability_v2.py +98 -0
  67. creatorsapi_python_sdk/models/offer_condition_v2.py +96 -0
  68. creatorsapi_python_sdk/models/offer_listing_v2.py +133 -0
  69. creatorsapi_python_sdk/models/offer_loyalty_points_v2.py +92 -0
  70. creatorsapi_python_sdk/models/offer_merchant_info_v2.py +94 -0
  71. creatorsapi_python_sdk/models/offer_price_v2.py +113 -0
  72. creatorsapi_python_sdk/models/offer_saving_basis.py +101 -0
  73. creatorsapi_python_sdk/models/offer_savings.py +98 -0
  74. creatorsapi_python_sdk/models/offer_type.py +45 -0
  75. creatorsapi_python_sdk/models/offers_v2.py +100 -0
  76. creatorsapi_python_sdk/models/product_info.py +124 -0
  77. creatorsapi_python_sdk/models/rating.py +92 -0
  78. creatorsapi_python_sdk/models/refinement.py +104 -0
  79. creatorsapi_python_sdk/models/refinement_bin.py +94 -0
  80. creatorsapi_python_sdk/models/report_metadata.py +98 -0
  81. creatorsapi_python_sdk/models/resource_not_found_exception_response_content.py +98 -0
  82. creatorsapi_python_sdk/models/saving_basis_type.py +46 -0
  83. creatorsapi_python_sdk/models/search_items_request_content.py +242 -0
  84. creatorsapi_python_sdk/models/search_items_resource.py +77 -0
  85. creatorsapi_python_sdk/models/search_items_response_content.py +106 -0
  86. creatorsapi_python_sdk/models/search_refinements.py +110 -0
  87. creatorsapi_python_sdk/models/search_result.py +110 -0
  88. creatorsapi_python_sdk/models/single_boolean_valued_attribute.py +96 -0
  89. creatorsapi_python_sdk/models/single_integer_valued_attribute.py +96 -0
  90. creatorsapi_python_sdk/models/single_string_valued_attribute.py +96 -0
  91. creatorsapi_python_sdk/models/sort_by.py +48 -0
  92. creatorsapi_python_sdk/models/technical_info.py +102 -0
  93. creatorsapi_python_sdk/models/throttle_exception_response_content.py +98 -0
  94. creatorsapi_python_sdk/models/trade_in_info.py +98 -0
  95. creatorsapi_python_sdk/models/trade_in_price.py +96 -0
  96. creatorsapi_python_sdk/models/unauthorized_exception_reason.py +51 -0
  97. creatorsapi_python_sdk/models/unauthorized_exception_response_content.py +97 -0
  98. creatorsapi_python_sdk/models/unit_based_attribute.py +98 -0
  99. creatorsapi_python_sdk/models/validation_exception_field.py +94 -0
  100. creatorsapi_python_sdk/models/validation_exception_reason.py +48 -0
  101. creatorsapi_python_sdk/models/validation_exception_response_content.py +107 -0
  102. creatorsapi_python_sdk/models/variation_attribute.py +94 -0
  103. creatorsapi_python_sdk/models/variation_dimension.py +98 -0
  104. creatorsapi_python_sdk/models/variation_summary.py +104 -0
  105. creatorsapi_python_sdk/models/variations_result.py +106 -0
  106. creatorsapi_python_sdk/models/website_sales_rank.py +98 -0
  107. creatorsapi_python_sdk/py.typed +0 -0
  108. creatorsapi_python_sdk/rest.py +262 -0
@@ -0,0 +1,912 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
+
6
+ Licensed under the Apache License, Version 2.0 (the "License").
7
+ You may not use this file except in compliance with the License.
8
+ A copy of the License is located at
9
+
10
+ http://www.apache.org/licenses/LICENSE-2.0
11
+
12
+ or in the "license" file accompanying this file. This file is distributed
13
+ on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14
+ express or implied. See the License for the specific language governing
15
+ permissions and limitations under the License.
16
+
17
+ """ # noqa: E501
18
+
19
+
20
+ import datetime
21
+ from dateutil.parser import parse
22
+ import threading
23
+ from enum import Enum
24
+ import decimal
25
+ import json
26
+ import mimetypes
27
+ import os
28
+ import re
29
+ import tempfile
30
+
31
+ from urllib.parse import quote
32
+ from typing import Tuple, Optional, List, Dict, Union
33
+ from pydantic import SecretStr
34
+
35
+ from creatorsapi_python_sdk.configuration import Configuration
36
+ from creatorsapi_python_sdk.api_response import ApiResponse, T as ApiResponseT
37
+ import creatorsapi_python_sdk.models
38
+ from creatorsapi_python_sdk import rest
39
+ from creatorsapi_python_sdk.exceptions import (
40
+ ApiValueError,
41
+ ApiException,
42
+ BadRequestException,
43
+ UnauthorizedException,
44
+ ForbiddenException,
45
+ NotFoundException,
46
+ ServiceException
47
+ )
48
+ from creatorsapi_python_sdk.auth.oauth2_config import OAuth2Config
49
+ from creatorsapi_python_sdk.auth.oauth2_token_manager import OAuth2TokenManager
50
+
51
+ RequestSerialized = Tuple[str, str, Dict[str, str], Optional[str], List[str]]
52
+
53
+ class ApiClient:
54
+ """Generic API client for OpenAPI client library builds.
55
+
56
+ OpenAPI generic API client. This client handles the client-
57
+ server communication, and is invariant across implementations. Specifics of
58
+ the methods and models for each application are generated from the OpenAPI
59
+ templates.
60
+
61
+ :param configuration: .Configuration object for this client
62
+ :param header_name: a header to pass when making calls to the API.
63
+ :param header_value: a header value to pass when making calls to
64
+ the API.
65
+ :param cookie: a cookie to include in the header when making calls
66
+ to the API https://creatorsapi.amazon
67
+ """
68
+
69
+ PRIMITIVE_TYPES = (float, bool, bytes, str, int)
70
+ NATIVE_TYPES_MAPPING = {
71
+ 'int': int,
72
+ 'long': int,
73
+ 'float': float,
74
+ 'str': str,
75
+ 'bool': bool,
76
+ 'date': datetime.date,
77
+ 'datetime': datetime.datetime,
78
+ 'decimal': decimal.Decimal,
79
+ 'object': object,
80
+ }
81
+ _pool = None
82
+
83
+ def __init__(
84
+ self,
85
+ configuration=None,
86
+ header_name: Optional[str] = None,
87
+ header_value: Optional[str] = None,
88
+ cookie: Optional[str] = None,
89
+ credential_id: Optional[str] = None,
90
+ credential_secret: Optional[str] = None,
91
+ version: Optional[str] = None,
92
+ host="https://creatorsapi.amazon",
93
+ auth_endpoint: Optional[str] = None
94
+ ) -> None:
95
+ # use default configuration if none is provided
96
+ if configuration is None:
97
+ configuration = Configuration.get_default()
98
+
99
+ # Set host (use default if not provided)
100
+ configuration.host = host
101
+
102
+ self.configuration = configuration
103
+
104
+ self.rest_client = rest.RESTClientObject(configuration)
105
+ self.default_headers = {}
106
+ if header_name is not None:
107
+ self.default_headers[header_name] = header_value
108
+ self.cookie = cookie
109
+ # Set default User-Agent.
110
+ self.user_agent = 'creatorsapi-python-sdk/1.1.2'
111
+ self.client_side_validation = configuration.client_side_validation
112
+
113
+ # OAuth2 properties
114
+ self._credential_id: Optional[str] = credential_id
115
+ self._credential_secret: Optional[str] = credential_secret
116
+ self._version: Optional[str] = version
117
+ self._auth_endpoint: Optional[str] = auth_endpoint
118
+
119
+ # OAuth2 token manager - reused across requests for token caching
120
+ self._token_manager: Optional[OAuth2TokenManager] = None
121
+ self._token_manager_lock: threading.Lock = threading.Lock()
122
+
123
+ @property
124
+ def credential_id(self) -> Optional[str]:
125
+ """Credential ID."""
126
+ return self._credential_id
127
+
128
+ @credential_id.setter
129
+ def credential_id(self, value: Optional[str]) -> None:
130
+ """Set credential ID. Invalidates token cache if changed."""
131
+ if value != self._credential_id:
132
+ self._token_manager = None
133
+ self._credential_id = value
134
+
135
+ @property
136
+ def credential_secret(self) -> Optional[str]:
137
+ """Credential secret."""
138
+ return self._credential_secret
139
+
140
+ @credential_secret.setter
141
+ def credential_secret(self, value: Optional[str]) -> None:
142
+ """Set credential secret. Invalidates token cache if changed."""
143
+ if value != self._credential_secret:
144
+ self._token_manager = None
145
+ self._credential_secret = value
146
+
147
+ @property
148
+ def version(self) -> Optional[str]:
149
+ """API version."""
150
+ return self._version
151
+
152
+ @version.setter
153
+ def version(self, value: Optional[str]) -> None:
154
+ """Set API version. Invalidates token cache if changed."""
155
+ if value != self._version:
156
+ self._token_manager = None
157
+ self._version = value
158
+
159
+ @property
160
+ def auth_endpoint(self) -> Optional[str]:
161
+ """OAuth2 auth endpoint URL."""
162
+ return self._auth_endpoint
163
+
164
+ @auth_endpoint.setter
165
+ def auth_endpoint(self, value: Optional[str]) -> None:
166
+ """Set auth endpoint. Invalidates token cache if changed."""
167
+ if value != self._auth_endpoint:
168
+ self._token_manager = None
169
+ self._auth_endpoint = value
170
+
171
+
172
+ @property
173
+ def token_manager(self) -> Optional[OAuth2TokenManager]:
174
+ """OAuth2 token manager (read-only, managed automatically)."""
175
+ return self._token_manager
176
+
177
+ def __enter__(self):
178
+ return self
179
+
180
+ def __exit__(self, exc_type, exc_value, traceback):
181
+ pass
182
+
183
+ @property
184
+ def user_agent(self):
185
+ """User agent for this API client"""
186
+ return self.default_headers['User-Agent']
187
+
188
+ @user_agent.setter
189
+ def user_agent(self, value):
190
+ self.default_headers['User-Agent'] = value
191
+
192
+ def set_default_header(self, header_name, header_value):
193
+ self.default_headers[header_name] = header_value
194
+
195
+ def get_auth_endpoint(self):
196
+ """Get the auth endpoint URL"""
197
+ return self.auth_endpoint
198
+
199
+ def set_auth_endpoint(self, auth_endpoint):
200
+ """Set the auth endpoint URL"""
201
+ self.auth_endpoint = auth_endpoint
202
+
203
+
204
+ _default = None
205
+
206
+ @classmethod
207
+ def get_default(cls):
208
+ """Return new instance of ApiClient.
209
+
210
+ This method returns newly created, based on default constructor,
211
+ object of ApiClient class or returns a copy of default
212
+ ApiClient.
213
+
214
+ :return: The ApiClient object.
215
+ """
216
+ if cls._default is None:
217
+ cls._default = ApiClient()
218
+ return cls._default
219
+
220
+ @classmethod
221
+ def set_default(cls, default):
222
+ """Set default instance of ApiClient.
223
+
224
+ It stores default ApiClient.
225
+
226
+ :param default: object of ApiClient.
227
+ """
228
+ cls._default = default
229
+
230
+ def param_serialize(
231
+ self,
232
+ method,
233
+ resource_path,
234
+ path_params=None,
235
+ query_params=None,
236
+ header_params=None,
237
+ body=None,
238
+ post_params=None,
239
+ files=None, auth_settings=None,
240
+ collection_formats=None,
241
+ _host=None,
242
+ _request_auth=None
243
+ ) -> RequestSerialized:
244
+
245
+ """Builds the HTTP request params needed by the request.
246
+ :param method: Method to call.
247
+ :param resource_path: Path to method endpoint.
248
+ :param path_params: Path parameters in the url.
249
+ :param query_params: Query parameters in the url.
250
+ :param header_params: Header parameters to be
251
+ placed in the request header.
252
+ :param body: Request body.
253
+ :param post_params dict: Request post form parameters,
254
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
255
+ :param auth_settings list: Auth Settings names for the request.
256
+ :param files dict: key -> filename, value -> filepath,
257
+ for `multipart/form-data`.
258
+ :param collection_formats: dict of collection formats for path, query,
259
+ header, and post parameters.
260
+ :param _request_auth: set to override the auth_settings for an a single
261
+ request; this effectively ignores the authentication
262
+ in the spec for a single request.
263
+ :return: tuple of form (path, http_method, query_params, header_params,
264
+ body, post_params, files)
265
+ """
266
+
267
+ config = self.configuration
268
+
269
+ # header parameters
270
+ header_params = header_params or {}
271
+ header_params.update(self.default_headers)
272
+ if self.cookie:
273
+ header_params['Cookie'] = self.cookie
274
+ if header_params:
275
+ header_params = self.sanitize_for_serialization(header_params)
276
+ header_params = dict(
277
+ self.parameters_to_tuples(header_params,collection_formats)
278
+ )
279
+
280
+ # path parameters
281
+ if path_params:
282
+ path_params = self.sanitize_for_serialization(path_params)
283
+ path_params = self.parameters_to_tuples(
284
+ path_params,
285
+ collection_formats
286
+ )
287
+ for k, v in path_params:
288
+ # specified safe chars, encode everything
289
+ resource_path = resource_path.replace(
290
+ '{%s}' % k,
291
+ quote(str(v), safe=config.safe_chars_for_path_param)
292
+ )
293
+
294
+ # post parameters
295
+ if post_params or files:
296
+ post_params = post_params if post_params else []
297
+ post_params = self.sanitize_for_serialization(post_params)
298
+ post_params = self.parameters_to_tuples(
299
+ post_params,
300
+ collection_formats
301
+ )
302
+ if files:
303
+ post_params.extend(self.files_parameters(files))
304
+
305
+ # auth setting
306
+ self.update_params_for_auth(
307
+ header_params,
308
+ query_params,
309
+ auth_settings,
310
+ resource_path,
311
+ method,
312
+ body,
313
+ request_auth=_request_auth
314
+ )
315
+
316
+ # body
317
+ if body:
318
+ body = self.sanitize_for_serialization(body)
319
+
320
+ # request url
321
+ if _host is None or self.configuration.ignore_operation_servers:
322
+ url = self.configuration.host + resource_path
323
+ else:
324
+ # use server/host defined in path or operation instead
325
+ url = _host + resource_path
326
+
327
+ # query parameters
328
+ if query_params:
329
+ query_params = self.sanitize_for_serialization(query_params)
330
+ url_query = self.parameters_to_url_query(
331
+ query_params,
332
+ collection_formats
333
+ )
334
+ url += "?" + url_query
335
+
336
+ return method, url, header_params, body, post_params
337
+
338
+
339
+ def call_api(
340
+ self,
341
+ method,
342
+ url,
343
+ header_params=None,
344
+ body=None,
345
+ post_params=None,
346
+ _request_timeout=None
347
+ ) -> rest.RESTResponse:
348
+ """Makes the HTTP request (synchronous)
349
+ :param method: Method to call.
350
+ :param url: Path to method endpoint.
351
+ :param header_params: Header parameters to be
352
+ placed in the request header.
353
+ :param body: Request body.
354
+ :param post_params dict: Request post form parameters,
355
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
356
+ :param _request_timeout: timeout setting for this request.
357
+ :return: RESTResponse
358
+ """
359
+
360
+ # Validate OAuth2 configuration
361
+ if not self.credential_id or not self.credential_secret or not self.version:
362
+ raise ValueError("Missing configuration. Please specify credential_id, credential_secret, and version.")
363
+
364
+ # Handle POST requests with null/undefined body - convert to empty object
365
+ if method.upper() == 'POST' and body is None:
366
+ body = {}
367
+
368
+ # Initialize headers
369
+ if not header_params:
370
+ header_params = {}
371
+
372
+ # Add Content-Type if not present
373
+ if 'Content-Type' not in header_params:
374
+ header_params['Content-Type'] = 'application/json; charset=utf-8'
375
+
376
+ # Get OAuth2 token (cached after first fetch) and add Authorization header
377
+ try:
378
+ # Initialize OAuth2TokenManager once - reused across requests for token caching
379
+ if self._token_manager is None:
380
+ with self._token_manager_lock:
381
+ # Double-check after acquiring lock
382
+ if self._token_manager is None:
383
+ config = OAuth2Config(
384
+ self.credential_id, self.credential_secret,
385
+ self.version, self.auth_endpoint
386
+ )
387
+ self._token_manager = OAuth2TokenManager(config)
388
+ # Get token (will use cached token if valid)
389
+ token = self._token_manager.get_token()
390
+ # Add Authorization headers
391
+ header_params['Authorization'] = 'Bearer {}, Version {}'.format(token, self.version)
392
+ except Exception as error:
393
+ raise error
394
+
395
+ try:
396
+ # perform request and return response
397
+ response_data = self.rest_client.request(
398
+ method, url,
399
+ headers=header_params,
400
+ body=body, post_params=post_params,
401
+ _request_timeout=_request_timeout
402
+ )
403
+
404
+ except ApiException as e:
405
+ raise e
406
+
407
+ return response_data
408
+
409
+ def response_deserialize(
410
+ self,
411
+ response_data: rest.RESTResponse,
412
+ response_types_map: Optional[Dict[str, ApiResponseT]]=None
413
+ ) -> ApiResponse[ApiResponseT]:
414
+ """Deserializes response into an object.
415
+ :param response_data: RESTResponse object to be deserialized.
416
+ :param response_types_map: dict of response types.
417
+ :return: ApiResponse
418
+ """
419
+
420
+ msg = "RESTResponse.read() must be called before passing it to response_deserialize()"
421
+ assert response_data.data is not None, msg
422
+
423
+ response_type = response_types_map.get(str(response_data.status), None)
424
+ if not response_type and isinstance(response_data.status, int) and 100 <= response_data.status <= 599:
425
+ # if not found, look for '1XX', '2XX', etc.
426
+ response_type = response_types_map.get(str(response_data.status)[0] + "XX", None)
427
+
428
+ # deserialize response data
429
+ response_text = None
430
+ return_data = None
431
+ try:
432
+ if response_type == "bytearray":
433
+ return_data = response_data.data
434
+ elif response_type == "file":
435
+ return_data = self.__deserialize_file(response_data)
436
+ elif response_type is not None:
437
+ match = None
438
+ content_type = response_data.getheader('content-type')
439
+ if content_type is not None:
440
+ match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type)
441
+ encoding = match.group(1) if match else "utf-8"
442
+ response_text = response_data.data.decode(encoding)
443
+ return_data = self.deserialize(response_text, response_type, content_type)
444
+ finally:
445
+ if not 200 <= response_data.status <= 299:
446
+ raise ApiException.from_response(
447
+ http_resp=response_data,
448
+ body=response_text,
449
+ data=return_data,
450
+ )
451
+
452
+ return ApiResponse(
453
+ status_code = response_data.status,
454
+ data = return_data,
455
+ headers = response_data.getheaders(),
456
+ raw_data = response_data.data
457
+ )
458
+
459
+ def sanitize_for_serialization(self, obj):
460
+ """Builds a JSON POST object.
461
+
462
+ If obj is None, return None.
463
+ If obj is SecretStr, return obj.get_secret_value()
464
+ If obj is str, int, long, float, bool, return directly.
465
+ If obj is datetime.datetime, datetime.date
466
+ convert to string in iso8601 format.
467
+ If obj is decimal.Decimal return string representation.
468
+ If obj is list, sanitize each element in the list.
469
+ If obj is dict, return the dict.
470
+ If obj is OpenAPI model, return the properties dict.
471
+
472
+ :param obj: The data to serialize.
473
+ :return: The serialized form of data.
474
+ """
475
+ if obj is None:
476
+ return None
477
+ elif isinstance(obj, Enum):
478
+ return obj.value
479
+ elif isinstance(obj, SecretStr):
480
+ return obj.get_secret_value()
481
+ elif isinstance(obj, self.PRIMITIVE_TYPES):
482
+ return obj
483
+ elif isinstance(obj, list):
484
+ return [
485
+ self.sanitize_for_serialization(sub_obj) for sub_obj in obj
486
+ ]
487
+ elif isinstance(obj, tuple):
488
+ return tuple(
489
+ self.sanitize_for_serialization(sub_obj) for sub_obj in obj
490
+ )
491
+ elif isinstance(obj, (datetime.datetime, datetime.date)):
492
+ return obj.isoformat()
493
+ elif isinstance(obj, decimal.Decimal):
494
+ return str(obj)
495
+
496
+ elif isinstance(obj, dict):
497
+ obj_dict = obj
498
+ else:
499
+ # Convert model obj to dict except
500
+ # attributes `openapi_types`, `attribute_map`
501
+ # and attributes which value is not None.
502
+ # Convert attribute name to json key in
503
+ # model definition for request.
504
+ if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')):
505
+ obj_dict = obj.to_dict()
506
+ else:
507
+ obj_dict = obj.__dict__
508
+
509
+ return {
510
+ key: self.sanitize_for_serialization(val)
511
+ for key, val in obj_dict.items()
512
+ }
513
+
514
+ def deserialize(self, response_text: str, response_type: str, content_type: Optional[str]):
515
+ """Deserializes response into an object.
516
+
517
+ :param response: RESTResponse object to be deserialized.
518
+ :param response_type: class literal for
519
+ deserialized object, or string of class name.
520
+ :param content_type: content type of response.
521
+
522
+ :return: deserialized object.
523
+ """
524
+
525
+ # fetch data from response object
526
+ if content_type is None:
527
+ try:
528
+ data = json.loads(response_text)
529
+ except ValueError:
530
+ data = response_text
531
+ elif content_type.startswith("application/json"):
532
+ if response_text == "":
533
+ data = ""
534
+ else:
535
+ data = json.loads(response_text)
536
+ elif content_type.startswith("text/plain"):
537
+ data = response_text
538
+ else:
539
+ raise ApiException(
540
+ status=0,
541
+ reason="Unsupported content type: {0}".format(content_type)
542
+ )
543
+
544
+ return self.__deserialize(data, response_type)
545
+
546
+ def __deserialize(self, data, klass):
547
+ """Deserializes dict, list, str into an object.
548
+
549
+ :param data: dict, list or str.
550
+ :param klass: class literal, or string of class name.
551
+
552
+ :return: object.
553
+ """
554
+ if data is None:
555
+ return None
556
+
557
+ if isinstance(klass, str):
558
+ if klass.startswith('List['):
559
+ m = re.match(r'List\[(.*)]', klass)
560
+ assert m is not None, "Malformed List type definition"
561
+ sub_kls = m.group(1)
562
+ return [self.__deserialize(sub_data, sub_kls)
563
+ for sub_data in data]
564
+
565
+ if klass.startswith('Dict['):
566
+ m = re.match(r'Dict\[([^,]*), (.*)]', klass)
567
+ assert m is not None, "Malformed Dict type definition"
568
+ sub_kls = m.group(2)
569
+ return {k: self.__deserialize(v, sub_kls)
570
+ for k, v in data.items()}
571
+
572
+ # convert str to class
573
+ if klass in self.NATIVE_TYPES_MAPPING:
574
+ klass = self.NATIVE_TYPES_MAPPING[klass]
575
+ else:
576
+ klass = getattr(creatorsapi_python_sdk.models, klass)
577
+
578
+ if klass in self.PRIMITIVE_TYPES:
579
+ return self.__deserialize_primitive(data, klass)
580
+ elif klass == object:
581
+ return self.__deserialize_object(data)
582
+ elif klass == datetime.date:
583
+ return self.__deserialize_date(data)
584
+ elif klass == datetime.datetime:
585
+ return self.__deserialize_datetime(data)
586
+ elif klass == decimal.Decimal:
587
+ return decimal.Decimal(data)
588
+ elif issubclass(klass, Enum):
589
+ return self.__deserialize_enum(data, klass)
590
+ else:
591
+ return self.__deserialize_model(data, klass)
592
+
593
+ def parameters_to_tuples(self, params, collection_formats):
594
+ """Get parameters as list of tuples, formatting collections.
595
+
596
+ :param params: Parameters as dict or list of two-tuples
597
+ :param dict collection_formats: Parameter collection formats
598
+ :return: Parameters as list of tuples, collections formatted
599
+ """
600
+ new_params: List[Tuple[str, str]] = []
601
+ if collection_formats is None:
602
+ collection_formats = {}
603
+ for k, v in params.items() if isinstance(params, dict) else params:
604
+ if k in collection_formats:
605
+ collection_format = collection_formats[k]
606
+ if collection_format == 'multi':
607
+ new_params.extend((k, value) for value in v)
608
+ else:
609
+ if collection_format == 'ssv':
610
+ delimiter = ' '
611
+ elif collection_format == 'tsv':
612
+ delimiter = '\t'
613
+ elif collection_format == 'pipes':
614
+ delimiter = '|'
615
+ else: # csv is the default
616
+ delimiter = ','
617
+ new_params.append(
618
+ (k, delimiter.join(str(value) for value in v)))
619
+ else:
620
+ new_params.append((k, v))
621
+ return new_params
622
+
623
+ def parameters_to_url_query(self, params, collection_formats):
624
+ """Get parameters as list of tuples, formatting collections.
625
+
626
+ :param params: Parameters as dict or list of two-tuples
627
+ :param dict collection_formats: Parameter collection formats
628
+ :return: URL query string (e.g. a=Hello%20World&b=123)
629
+ """
630
+ new_params: List[Tuple[str, str]] = []
631
+ if collection_formats is None:
632
+ collection_formats = {}
633
+ for k, v in params.items() if isinstance(params, dict) else params:
634
+ if isinstance(v, bool):
635
+ v = str(v).lower()
636
+ if isinstance(v, (int, float)):
637
+ v = str(v)
638
+ if isinstance(v, dict):
639
+ v = json.dumps(v)
640
+
641
+ if k in collection_formats:
642
+ collection_format = collection_formats[k]
643
+ if collection_format == 'multi':
644
+ new_params.extend((k, str(value)) for value in v)
645
+ else:
646
+ if collection_format == 'ssv':
647
+ delimiter = ' '
648
+ elif collection_format == 'tsv':
649
+ delimiter = '\t'
650
+ elif collection_format == 'pipes':
651
+ delimiter = '|'
652
+ else: # csv is the default
653
+ delimiter = ','
654
+ new_params.append(
655
+ (k, delimiter.join(quote(str(value)) for value in v))
656
+ )
657
+ else:
658
+ new_params.append((k, quote(str(v))))
659
+
660
+ return "&".join(["=".join(map(str, item)) for item in new_params])
661
+
662
+ def files_parameters(self, files: Dict[str, Union[str, bytes]]):
663
+ """Builds form parameters.
664
+
665
+ :param files: File parameters.
666
+ :return: Form parameters with files.
667
+ """
668
+ params = []
669
+ for k, v in files.items():
670
+ if isinstance(v, str):
671
+ with open(v, 'rb') as f:
672
+ filename = os.path.basename(f.name)
673
+ filedata = f.read()
674
+ elif isinstance(v, bytes):
675
+ filename = k
676
+ filedata = v
677
+ else:
678
+ raise ValueError("Unsupported file value")
679
+ mimetype = (
680
+ mimetypes.guess_type(filename)[0]
681
+ or 'application/octet-stream'
682
+ )
683
+ params.append(
684
+ tuple([k, tuple([filename, filedata, mimetype])])
685
+ )
686
+ return params
687
+
688
+ def select_header_accept(self, accepts: List[str]) -> Optional[str]:
689
+ """Returns `Accept` based on an array of accepts provided.
690
+
691
+ :param accepts: List of headers.
692
+ :return: Accept (e.g. application/json).
693
+ """
694
+ if not accepts:
695
+ return None
696
+
697
+ for accept in accepts:
698
+ if re.search('json', accept, re.IGNORECASE):
699
+ return accept
700
+
701
+ return accepts[0]
702
+
703
+ def select_header_content_type(self, content_types):
704
+ """Returns `Content-Type` based on an array of content_types provided.
705
+
706
+ :param content_types: List of content-types.
707
+ :return: Content-Type (e.g. application/json).
708
+ """
709
+ if not content_types:
710
+ return None
711
+
712
+ for content_type in content_types:
713
+ if re.search('json', content_type, re.IGNORECASE):
714
+ return content_type
715
+
716
+ return content_types[0]
717
+
718
+ def update_params_for_auth(
719
+ self,
720
+ headers,
721
+ queries,
722
+ auth_settings,
723
+ resource_path,
724
+ method,
725
+ body,
726
+ request_auth=None
727
+ ) -> None:
728
+ """Updates header and query params based on authentication setting.
729
+
730
+ :param headers: Header parameters dict to be updated.
731
+ :param queries: Query parameters tuple list to be updated.
732
+ :param auth_settings: Authentication setting identifiers list.
733
+ :resource_path: A string representation of the HTTP request resource path.
734
+ :method: A string representation of the HTTP request method.
735
+ :body: A object representing the body of the HTTP request.
736
+ The object type is the return value of sanitize_for_serialization().
737
+ :param request_auth: if set, the provided settings will
738
+ override the token in the configuration.
739
+ """
740
+ if not auth_settings:
741
+ return
742
+
743
+ if request_auth:
744
+ self._apply_auth_params(
745
+ headers,
746
+ queries,
747
+ resource_path,
748
+ method,
749
+ body,
750
+ request_auth
751
+ )
752
+ else:
753
+ for auth in auth_settings:
754
+ auth_setting = self.configuration.auth_settings().get(auth)
755
+ if auth_setting:
756
+ self._apply_auth_params(
757
+ headers,
758
+ queries,
759
+ resource_path,
760
+ method,
761
+ body,
762
+ auth_setting
763
+ )
764
+
765
+ def _apply_auth_params(
766
+ self,
767
+ headers,
768
+ queries,
769
+ resource_path,
770
+ method,
771
+ body,
772
+ auth_setting
773
+ ) -> None:
774
+ """Updates the request parameters based on a single auth_setting
775
+
776
+ :param headers: Header parameters dict to be updated.
777
+ :param queries: Query parameters tuple list to be updated.
778
+ :resource_path: A string representation of the HTTP request resource path.
779
+ :method: A string representation of the HTTP request method.
780
+ :body: A object representing the body of the HTTP request.
781
+ The object type is the return value of sanitize_for_serialization().
782
+ :param auth_setting: auth settings for the endpoint
783
+ """
784
+ if auth_setting['in'] == 'cookie':
785
+ headers['Cookie'] = auth_setting['value']
786
+ elif auth_setting['in'] == 'header':
787
+ if auth_setting['type'] != 'http-signature':
788
+ headers[auth_setting['key']] = auth_setting['value']
789
+ elif auth_setting['in'] == 'query':
790
+ queries.append((auth_setting['key'], auth_setting['value']))
791
+ else:
792
+ raise ApiValueError(
793
+ 'Authentication token must be in `query` or `header`'
794
+ )
795
+
796
+ def __deserialize_file(self, response):
797
+ """Deserializes body to file
798
+
799
+ Saves response body into a file in a temporary folder,
800
+ using the filename from the `Content-Disposition` header if provided.
801
+
802
+ handle file downloading
803
+ save response body into a tmp file and return the instance
804
+
805
+ :param response: RESTResponse.
806
+ :return: file path.
807
+ """
808
+ fd, path = tempfile.mkstemp(dir=self.configuration.temp_folder_path)
809
+ os.close(fd)
810
+ os.remove(path)
811
+
812
+ content_disposition = response.getheader("Content-Disposition")
813
+ if content_disposition:
814
+ m = re.search(
815
+ r'filename=[\'"]?([^\'"\s]+)[\'"]?',
816
+ content_disposition
817
+ )
818
+ assert m is not None, "Unexpected 'content-disposition' header value"
819
+ filename = m.group(1)
820
+ path = os.path.join(os.path.dirname(path), filename)
821
+
822
+ with open(path, "wb") as f:
823
+ f.write(response.data)
824
+
825
+ return path
826
+
827
+ def __deserialize_primitive(self, data, klass):
828
+ """Deserializes string to primitive type.
829
+
830
+ :param data: str.
831
+ :param klass: class literal.
832
+
833
+ :return: int, long, float, str, bool.
834
+ """
835
+ try:
836
+ return klass(data)
837
+ except UnicodeEncodeError:
838
+ return str(data)
839
+ except TypeError:
840
+ return data
841
+
842
+ def __deserialize_object(self, value):
843
+ """Return an original value.
844
+
845
+ :return: object.
846
+ """
847
+ return value
848
+
849
+ def __deserialize_date(self, string):
850
+ """Deserializes string to date.
851
+
852
+ :param string: str.
853
+ :return: date.
854
+ """
855
+ try:
856
+ return parse(string).date()
857
+ except ImportError:
858
+ return string
859
+ except ValueError:
860
+ raise rest.ApiException(
861
+ status=0,
862
+ reason="Failed to parse `{0}` as date object".format(string)
863
+ )
864
+
865
+ def __deserialize_datetime(self, string):
866
+ """Deserializes string to datetime.
867
+
868
+ The string should be in iso8601 datetime format.
869
+
870
+ :param string: str.
871
+ :return: datetime.
872
+ """
873
+ try:
874
+ return parse(string)
875
+ except ImportError:
876
+ return string
877
+ except ValueError:
878
+ raise rest.ApiException(
879
+ status=0,
880
+ reason=(
881
+ "Failed to parse `{0}` as datetime object"
882
+ .format(string)
883
+ )
884
+ )
885
+
886
+ def __deserialize_enum(self, data, klass):
887
+ """Deserializes primitive type to enum.
888
+
889
+ :param data: primitive type.
890
+ :param klass: class literal.
891
+ :return: enum value.
892
+ """
893
+ try:
894
+ return klass(data)
895
+ except ValueError:
896
+ raise rest.ApiException(
897
+ status=0,
898
+ reason=(
899
+ "Failed to parse `{0}` as `{1}`"
900
+ .format(data, klass)
901
+ )
902
+ )
903
+
904
+ def __deserialize_model(self, data, klass):
905
+ """Deserializes list or dict to model.
906
+
907
+ :param data: dict, list.
908
+ :param klass: class literal.
909
+ :return: model object.
910
+ """
911
+
912
+ return klass.from_dict(data)