wds-client 0.7.0__py3-none-any.whl → 0.9.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. wds_client/__init__.py +8 -10
  2. wds_client/api/__init__.py +1 -2
  3. wds_client/api/capabilities_api.py +237 -102
  4. wds_client/api/cloning_api.py +782 -329
  5. wds_client/api/general_wds_information_api.py +463 -191
  6. wds_client/api/import_api.py +288 -127
  7. wds_client/api/instances_api.py +783 -333
  8. wds_client/api/job_api.py +518 -215
  9. wds_client/api/records_api.py +2512 -1089
  10. wds_client/api/schema_api.py +1450 -626
  11. wds_client/api_client.py +414 -310
  12. wds_client/api_response.py +21 -0
  13. wds_client/configuration.py +110 -53
  14. wds_client/exceptions.py +99 -20
  15. wds_client/models/__init__.py +4 -8
  16. wds_client/models/app.py +68 -125
  17. wds_client/models/attribute_data_type.py +31 -94
  18. wds_client/models/attribute_schema.py +71 -157
  19. wds_client/models/attribute_schema_update.py +69 -127
  20. wds_client/models/backup_job.py +96 -298
  21. wds_client/models/backup_response.py +70 -157
  22. wds_client/models/backup_restore_request.py +68 -129
  23. wds_client/models/batch_operation.py +83 -137
  24. wds_client/models/batch_record_request.py +70 -160
  25. wds_client/models/batch_response.py +68 -127
  26. wds_client/models/build.py +79 -207
  27. wds_client/models/capabilities.py +83 -103
  28. wds_client/models/clone_job.py +96 -298
  29. wds_client/models/clone_response.py +68 -129
  30. wds_client/models/commit.py +69 -125
  31. wds_client/models/error_response.py +78 -222
  32. wds_client/models/generic_job.py +102 -334
  33. wds_client/models/git.py +76 -129
  34. wds_client/models/import_request.py +77 -165
  35. wds_client/models/job.py +87 -243
  36. wds_client/models/job_v1.py +97 -277
  37. wds_client/models/record_query_response.py +86 -162
  38. wds_client/models/record_request.py +60 -96
  39. wds_client/models/record_response.py +70 -160
  40. wds_client/models/record_type_schema.py +84 -191
  41. wds_client/models/search_filter.py +60 -95
  42. wds_client/models/search_request.py +84 -220
  43. wds_client/models/search_sort_direction.py +17 -80
  44. wds_client/models/status_response.py +68 -125
  45. wds_client/models/tsv_upload_response.py +68 -127
  46. wds_client/models/version_response.py +86 -155
  47. wds_client/py.typed +0 -0
  48. wds_client/rest.py +136 -170
  49. wds_client-0.9.0.dist-info/METADATA +17 -0
  50. wds_client-0.9.0.dist-info/RECORD +52 -0
  51. {wds_client-0.7.0.dist-info → wds_client-0.9.0.dist-info}/WHEEL +1 -1
  52. wds_client/models/backup_job_all_of.py +0 -148
  53. wds_client/models/clone_job_all_of.py +0 -148
  54. wds_client/models/generic_job_all_of.py +0 -150
  55. wds_client/models/inline_object.py +0 -123
  56. wds_client-0.7.0.dist-info/METADATA +0 -16
  57. wds_client-0.7.0.dist-info/RECORD +0 -54
  58. {wds_client-0.7.0.dist-info → wds_client-0.9.0.dist-info}/top_level.txt +0 -0
wds_client/api_client.py CHANGED
@@ -1,36 +1,47 @@
1
1
  # coding: utf-8
2
+
2
3
  """
3
4
  Workspace Data Service
4
5
 
5
- This page lists current APIs. As of v0.2, all APIs are subject to change without notice. # noqa: E501
6
+ This page lists current APIs. All v0.2 APIs are subject to change without notice. Changelog at [https://github.com/DataBiosphere/terra-workspace-data-service/releases](https://github.com/DataBiosphere/terra-workspace-data-service/releases)
6
7
 
7
8
  The version of the OpenAPI document: v0.2
8
- Generated by: https://openapi-generator.tech
9
- """
9
+ Generated by OpenAPI Generator (https://openapi-generator.tech)
10
+
11
+ Do not edit the class manually.
12
+ """ # noqa: E501
10
13
 
11
- from __future__ import absolute_import
12
14
 
13
- import atexit
14
15
  import datetime
15
16
  from dateutil.parser import parse
17
+ from enum import Enum
16
18
  import json
17
19
  import mimetypes
18
- from multiprocessing.pool import ThreadPool
19
20
  import os
20
21
  import re
21
22
  import tempfile
22
23
 
23
- # python 2 and python 3 compatibility library
24
- import six
25
- from six.moves.urllib.parse import quote
24
+ from urllib.parse import quote
25
+ from typing import Tuple, Optional, List, Dict, Union
26
+ from pydantic import SecretStr
26
27
 
27
28
  from wds_client.configuration import Configuration
29
+ from wds_client.api_response import ApiResponse, T as ApiResponseT
28
30
  import wds_client.models
29
31
  from wds_client import rest
30
- from wds_client.exceptions import ApiValueError, ApiException
31
-
32
-
33
- class ApiClient(object):
32
+ from wds_client.exceptions import (
33
+ ApiValueError,
34
+ ApiException,
35
+ BadRequestException,
36
+ UnauthorizedException,
37
+ ForbiddenException,
38
+ NotFoundException,
39
+ ServiceException
40
+ )
41
+
42
+ RequestSerialized = Tuple[str, str, Dict[str, str], Optional[str], List[str]]
43
+
44
+ class ApiClient:
34
45
  """Generic API client for OpenAPI client library builds.
35
46
 
36
47
  OpenAPI generic API client. This client handles the client-
@@ -38,24 +49,18 @@ class ApiClient(object):
38
49
  the methods and models for each application are generated from the OpenAPI
39
50
  templates.
40
51
 
41
- NOTE: This class is auto generated by OpenAPI Generator.
42
- Ref: https://openapi-generator.tech
43
- Do not edit the class manually.
44
-
45
52
  :param configuration: .Configuration object for this client
46
53
  :param header_name: a header to pass when making calls to the API.
47
54
  :param header_value: a header value to pass when making calls to
48
55
  the API.
49
56
  :param cookie: a cookie to include in the header when making calls
50
57
  to the API
51
- :param pool_threads: The number of threads to use for async requests
52
- to the API. More threads means more concurrent API requests.
53
58
  """
54
59
 
55
- PRIMITIVE_TYPES = (float, bool, bytes, six.text_type) + six.integer_types
60
+ PRIMITIVE_TYPES = (float, bool, bytes, str, int)
56
61
  NATIVE_TYPES_MAPPING = {
57
62
  'int': int,
58
- 'long': int if six.PY3 else long, # noqa: F821
63
+ 'long': int, # TODO remove as only py3 is supported?
59
64
  'float': float,
60
65
  'str': str,
61
66
  'bool': bool,
@@ -65,12 +70,17 @@ class ApiClient(object):
65
70
  }
66
71
  _pool = None
67
72
 
68
- def __init__(self, configuration=None, header_name=None, header_value=None,
69
- cookie=None, pool_threads=1):
73
+ def __init__(
74
+ self,
75
+ configuration=None,
76
+ header_name=None,
77
+ header_value=None,
78
+ cookie=None
79
+ ) -> None:
80
+ # use default configuration if none is provided
70
81
  if configuration is None:
71
- configuration = Configuration.get_default_copy()
82
+ configuration = Configuration.get_default()
72
83
  self.configuration = configuration
73
- self.pool_threads = pool_threads
74
84
 
75
85
  self.rest_client = rest.RESTClientObject(configuration)
76
86
  self.default_headers = {}
@@ -78,32 +88,14 @@ class ApiClient(object):
78
88
  self.default_headers[header_name] = header_value
79
89
  self.cookie = cookie
80
90
  # Set default User-Agent.
81
- self.user_agent = 'wds-client/0.7.0/python'
91
+ self.user_agent = 'wds-client/0.9.0/python'
82
92
  self.client_side_validation = configuration.client_side_validation
83
93
 
84
94
  def __enter__(self):
85
95
  return self
86
96
 
87
97
  def __exit__(self, exc_type, exc_value, traceback):
88
- self.close()
89
-
90
- def close(self):
91
- if self._pool:
92
- self._pool.close()
93
- self._pool.join()
94
- self._pool = None
95
- if hasattr(atexit, 'unregister'):
96
- atexit.unregister(self.close)
97
-
98
- @property
99
- def pool(self):
100
- """Create thread pool on first request
101
- avoids instantiating unused threadpool for blocking clients.
102
- """
103
- if self._pool is None:
104
- atexit.register(self.close)
105
- self._pool = ThreadPool(self.pool_threads)
106
- return self._pool
98
+ pass
107
99
 
108
100
  @property
109
101
  def user_agent(self):
@@ -117,12 +109,69 @@ class ApiClient(object):
117
109
  def set_default_header(self, header_name, header_value):
118
110
  self.default_headers[header_name] = header_value
119
111
 
120
- def __call_api(
121
- self, resource_path, method, path_params=None,
122
- query_params=None, header_params=None, body=None, post_params=None,
123
- files=None, response_type=None, auth_settings=None,
124
- _return_http_data_only=None, collection_formats=None,
125
- _preload_content=True, _request_timeout=None, _host=None):
112
+
113
+ _default = None
114
+
115
+ @classmethod
116
+ def get_default(cls):
117
+ """Return new instance of ApiClient.
118
+
119
+ This method returns newly created, based on default constructor,
120
+ object of ApiClient class or returns a copy of default
121
+ ApiClient.
122
+
123
+ :return: The ApiClient object.
124
+ """
125
+ if cls._default is None:
126
+ cls._default = ApiClient()
127
+ return cls._default
128
+
129
+ @classmethod
130
+ def set_default(cls, default):
131
+ """Set default instance of ApiClient.
132
+
133
+ It stores default ApiClient.
134
+
135
+ :param default: object of ApiClient.
136
+ """
137
+ cls._default = default
138
+
139
+ def param_serialize(
140
+ self,
141
+ method,
142
+ resource_path,
143
+ path_params=None,
144
+ query_params=None,
145
+ header_params=None,
146
+ body=None,
147
+ post_params=None,
148
+ files=None, auth_settings=None,
149
+ collection_formats=None,
150
+ _host=None,
151
+ _request_auth=None
152
+ ) -> RequestSerialized:
153
+
154
+ """Builds the HTTP request params needed by the request.
155
+ :param method: Method to call.
156
+ :param resource_path: Path to method endpoint.
157
+ :param path_params: Path parameters in the url.
158
+ :param query_params: Query parameters in the url.
159
+ :param header_params: Header parameters to be
160
+ placed in the request header.
161
+ :param body: Request body.
162
+ :param post_params dict: Request post form parameters,
163
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
164
+ :param auth_settings list: Auth Settings names for the request.
165
+ :param files dict: key -> filename, value -> filepath,
166
+ for `multipart/form-data`.
167
+ :param collection_formats: dict of collection formats for path, query,
168
+ header, and post parameters.
169
+ :param _request_auth: set to override the auth_settings for an a single
170
+ request; this effectively ignores the authentication
171
+ in the spec for a single request.
172
+ :return: tuple of form (path, http_method, query_params, header_params,
173
+ body, post_params, files)
174
+ """
126
175
 
127
176
  config = self.configuration
128
177
 
@@ -133,14 +182,17 @@ class ApiClient(object):
133
182
  header_params['Cookie'] = self.cookie
134
183
  if header_params:
135
184
  header_params = self.sanitize_for_serialization(header_params)
136
- header_params = dict(self.parameters_to_tuples(header_params,
137
- collection_formats))
185
+ header_params = dict(
186
+ self.parameters_to_tuples(header_params,collection_formats)
187
+ )
138
188
 
139
189
  # path parameters
140
190
  if path_params:
141
191
  path_params = self.sanitize_for_serialization(path_params)
142
- path_params = self.parameters_to_tuples(path_params,
143
- collection_formats)
192
+ path_params = self.parameters_to_tuples(
193
+ path_params,
194
+ collection_formats
195
+ )
144
196
  for k, v in path_params:
145
197
  # specified safe chars, encode everything
146
198
  resource_path = resource_path.replace(
@@ -148,22 +200,27 @@ class ApiClient(object):
148
200
  quote(str(v), safe=config.safe_chars_for_path_param)
149
201
  )
150
202
 
151
- # query parameters
152
- if query_params:
153
- query_params = self.sanitize_for_serialization(query_params)
154
- query_params = self.parameters_to_tuples(query_params,
155
- collection_formats)
156
-
157
203
  # post parameters
158
204
  if post_params or files:
159
205
  post_params = post_params if post_params else []
160
206
  post_params = self.sanitize_for_serialization(post_params)
161
- post_params = self.parameters_to_tuples(post_params,
162
- collection_formats)
163
- post_params.extend(self.files_parameters(files))
207
+ post_params = self.parameters_to_tuples(
208
+ post_params,
209
+ collection_formats
210
+ )
211
+ if files:
212
+ post_params.extend(self.files_parameters(files))
164
213
 
165
214
  # auth setting
166
- self.update_params_for_auth(header_params, query_params, auth_settings)
215
+ self.update_params_for_auth(
216
+ header_params,
217
+ query_params,
218
+ auth_settings,
219
+ resource_path,
220
+ method,
221
+ body,
222
+ request_auth=_request_auth
223
+ )
167
224
 
168
225
  # body
169
226
  if body:
@@ -176,49 +233,111 @@ class ApiClient(object):
176
233
  # use server/host defined in path or operation instead
177
234
  url = _host + resource_path
178
235
 
236
+ # query parameters
237
+ if query_params:
238
+ query_params = self.sanitize_for_serialization(query_params)
239
+ url_query = self.parameters_to_url_query(
240
+ query_params,
241
+ collection_formats
242
+ )
243
+ url += "?" + url_query
244
+
245
+ return method, url, header_params, body, post_params
246
+
247
+
248
+ def call_api(
249
+ self,
250
+ method,
251
+ url,
252
+ header_params=None,
253
+ body=None,
254
+ post_params=None,
255
+ _request_timeout=None
256
+ ) -> rest.RESTResponse:
257
+ """Makes the HTTP request (synchronous)
258
+ :param method: Method to call.
259
+ :param url: Path to method endpoint.
260
+ :param header_params: Header parameters to be
261
+ placed in the request header.
262
+ :param body: Request body.
263
+ :param post_params dict: Request post form parameters,
264
+ for `application/x-www-form-urlencoded`, `multipart/form-data`.
265
+ :param _request_timeout: timeout setting for this request.
266
+ :return: RESTResponse
267
+ """
268
+
179
269
  try:
180
270
  # perform request and return response
181
- response_data = self.request(
182
- method, url, query_params=query_params, headers=header_params,
183
- post_params=post_params, body=body,
184
- _preload_content=_preload_content,
185
- _request_timeout=_request_timeout)
271
+ response_data = self.rest_client.request(
272
+ method, url,
273
+ headers=header_params,
274
+ body=body, post_params=post_params,
275
+ _request_timeout=_request_timeout
276
+ )
277
+
186
278
  except ApiException as e:
187
- e.body = e.body.decode('utf-8') if six.PY3 else e.body
188
279
  raise e
189
280
 
190
- content_type = response_data.getheader('content-type')
191
-
192
- self.last_response = response_data
281
+ return response_data
193
282
 
194
- return_data = response_data
283
+ def response_deserialize(
284
+ self,
285
+ response_data: rest.RESTResponse,
286
+ response_types_map: Optional[Dict[str, ApiResponseT]]=None
287
+ ) -> ApiResponse[ApiResponseT]:
288
+ """Deserializes response into an object.
289
+ :param response_data: RESTResponse object to be deserialized.
290
+ :param response_types_map: dict of response types.
291
+ :return: ApiResponse
292
+ """
195
293
 
196
- if not _preload_content:
197
- return return_data
294
+ msg = "RESTResponse.read() must be called before passing it to response_deserialize()"
295
+ assert response_data.data is not None, msg
198
296
 
199
- if six.PY3 and response_type not in ["file", "bytes"]:
200
- match = None
201
- if content_type is not None:
202
- match = re.search(r"charset=([a-zA-Z\-\d]+)[\s\;]?", content_type)
203
- encoding = match.group(1) if match else "utf-8"
204
- response_data.data = response_data.data.decode(encoding)
297
+ response_type = response_types_map.get(str(response_data.status), None)
298
+ if not response_type and isinstance(response_data.status, int) and 100 <= response_data.status <= 599:
299
+ # if not found, look for '1XX', '2XX', etc.
300
+ response_type = response_types_map.get(str(response_data.status)[0] + "XX", None)
205
301
 
206
302
  # deserialize response data
207
- if response_type:
208
- return_data = self.deserialize(response_data, response_type)
209
- else:
210
- return_data = None
303
+ response_text = None
304
+ return_data = None
305
+ try:
306
+ if response_type == "bytearray":
307
+ return_data = response_data.data
308
+ elif response_type == "file":
309
+ return_data = self.__deserialize_file(response_data)
310
+ elif response_type is not None:
311
+ match = None
312
+ content_type = response_data.getheader('content-type')
313
+ if content_type is not None:
314
+ match = re.search(r"charset=([a-zA-Z\-\d]+)[\s;]?", content_type)
315
+ encoding = match.group(1) if match else "utf-8"
316
+ response_text = response_data.data.decode(encoding)
317
+ if response_type in ["bytearray", "str"]:
318
+ return_data = self.__deserialize_primitive(response_text, response_type)
319
+ else:
320
+ return_data = self.deserialize(response_text, response_type)
321
+ finally:
322
+ if not 200 <= response_data.status <= 299:
323
+ raise ApiException.from_response(
324
+ http_resp=response_data,
325
+ body=response_text,
326
+ data=return_data,
327
+ )
211
328
 
212
- if _return_http_data_only:
213
- return (return_data)
214
- else:
215
- return (return_data, response_data.status,
216
- response_data.getheaders())
329
+ return ApiResponse(
330
+ status_code = response_data.status,
331
+ data = return_data,
332
+ headers = response_data.getheaders(),
333
+ raw_data = response_data.data
334
+ )
217
335
 
218
336
  def sanitize_for_serialization(self, obj):
219
337
  """Builds a JSON POST object.
220
338
 
221
339
  If obj is None, return None.
340
+ If obj is SecretStr, return obj.get_secret_value()
222
341
  If obj is str, int, long, float, bool, return directly.
223
342
  If obj is datetime.datetime, datetime.date
224
343
  convert to string in iso8601 format.
@@ -231,18 +350,24 @@ class ApiClient(object):
231
350
  """
232
351
  if obj is None:
233
352
  return None
353
+ elif isinstance(obj, Enum):
354
+ return obj.value
355
+ elif isinstance(obj, SecretStr):
356
+ return obj.get_secret_value()
234
357
  elif isinstance(obj, self.PRIMITIVE_TYPES):
235
358
  return obj
236
359
  elif isinstance(obj, list):
237
- return [self.sanitize_for_serialization(sub_obj)
238
- for sub_obj in obj]
360
+ return [
361
+ self.sanitize_for_serialization(sub_obj) for sub_obj in obj
362
+ ]
239
363
  elif isinstance(obj, tuple):
240
- return tuple(self.sanitize_for_serialization(sub_obj)
241
- for sub_obj in obj)
364
+ return tuple(
365
+ self.sanitize_for_serialization(sub_obj) for sub_obj in obj
366
+ )
242
367
  elif isinstance(obj, (datetime.datetime, datetime.date)):
243
368
  return obj.isoformat()
244
369
 
245
- if isinstance(obj, dict):
370
+ elif isinstance(obj, dict):
246
371
  obj_dict = obj
247
372
  else:
248
373
  # Convert model obj to dict except
@@ -250,14 +375,17 @@ class ApiClient(object):
250
375
  # and attributes which value is not None.
251
376
  # Convert attribute name to json key in
252
377
  # model definition for request.
253
- obj_dict = {obj.attribute_map[attr]: getattr(obj, attr)
254
- for attr, _ in six.iteritems(obj.openapi_types)
255
- if getattr(obj, attr) is not None}
378
+ if hasattr(obj, 'to_dict') and callable(getattr(obj, 'to_dict')):
379
+ obj_dict = obj.to_dict()
380
+ else:
381
+ obj_dict = obj.__dict__
256
382
 
257
- return {key: self.sanitize_for_serialization(val)
258
- for key, val in six.iteritems(obj_dict)}
383
+ return {
384
+ key: self.sanitize_for_serialization(val)
385
+ for key, val in obj_dict.items()
386
+ }
259
387
 
260
- def deserialize(self, response, response_type):
388
+ def deserialize(self, response_text, response_type):
261
389
  """Deserializes response into an object.
262
390
 
263
391
  :param response: RESTResponse object to be deserialized.
@@ -266,16 +394,12 @@ class ApiClient(object):
266
394
 
267
395
  :return: deserialized object.
268
396
  """
269
- # handle file downloading
270
- # save response body into a tmp file and return the instance
271
- if response_type == "file":
272
- return self.__deserialize_file(response)
273
397
 
274
398
  # fetch data from response object
275
399
  try:
276
- data = json.loads(response.data)
400
+ data = json.loads(response_text)
277
401
  except ValueError:
278
- data = response.data
402
+ data = response_text
279
403
 
280
404
  return self.__deserialize(data, response_type)
281
405
 
@@ -290,16 +414,20 @@ class ApiClient(object):
290
414
  if data is None:
291
415
  return None
292
416
 
293
- if type(klass) == str:
294
- if klass.startswith('list['):
295
- sub_kls = re.match(r'list\[(.*)\]', klass).group(1)
417
+ if isinstance(klass, str):
418
+ if klass.startswith('List['):
419
+ m = re.match(r'List\[(.*)]', klass)
420
+ assert m is not None, "Malformed List type definition"
421
+ sub_kls = m.group(1)
296
422
  return [self.__deserialize(sub_data, sub_kls)
297
423
  for sub_data in data]
298
424
 
299
- if klass.startswith('dict('):
300
- sub_kls = re.match(r'dict\(([^,]*), (.*)\)', klass).group(2)
425
+ if klass.startswith('Dict['):
426
+ m = re.match(r'Dict\[([^,]*), (.*)]', klass)
427
+ assert m is not None, "Malformed Dict type definition"
428
+ sub_kls = m.group(2)
301
429
  return {k: self.__deserialize(v, sub_kls)
302
- for k, v in six.iteritems(data)}
430
+ for k, v in data.items()}
303
431
 
304
432
  # convert str to class
305
433
  if klass in self.NATIVE_TYPES_MAPPING:
@@ -315,131 +443,11 @@ class ApiClient(object):
315
443
  return self.__deserialize_date(data)
316
444
  elif klass == datetime.datetime:
317
445
  return self.__deserialize_datetime(data)
446
+ elif issubclass(klass, Enum):
447
+ return self.__deserialize_enum(data, klass)
318
448
  else:
319
449
  return self.__deserialize_model(data, klass)
320
450
 
321
- def call_api(self, resource_path, method,
322
- path_params=None, query_params=None, header_params=None,
323
- body=None, post_params=None, files=None,
324
- response_type=None, auth_settings=None, async_req=None,
325
- _return_http_data_only=None, collection_formats=None,
326
- _preload_content=True, _request_timeout=None, _host=None):
327
- """Makes the HTTP request (synchronous) and returns deserialized data.
328
-
329
- To make an async_req request, set the async_req parameter.
330
-
331
- :param resource_path: Path to method endpoint.
332
- :param method: Method to call.
333
- :param path_params: Path parameters in the url.
334
- :param query_params: Query parameters in the url.
335
- :param header_params: Header parameters to be
336
- placed in the request header.
337
- :param body: Request body.
338
- :param post_params dict: Request post form parameters,
339
- for `application/x-www-form-urlencoded`, `multipart/form-data`.
340
- :param auth_settings list: Auth Settings names for the request.
341
- :param response: Response data type.
342
- :param files dict: key -> filename, value -> filepath,
343
- for `multipart/form-data`.
344
- :param async_req bool: execute request asynchronously
345
- :param _return_http_data_only: response data without head status code
346
- and headers
347
- :param collection_formats: dict of collection formats for path, query,
348
- header, and post parameters.
349
- :param _preload_content: if False, the urllib3.HTTPResponse object will
350
- be returned without reading/decoding response
351
- data. Default is True.
352
- :param _request_timeout: timeout setting for this request. If one
353
- number provided, it will be total request
354
- timeout. It can also be a pair (tuple) of
355
- (connection, read) timeouts.
356
- :return:
357
- If async_req parameter is True,
358
- the request will be called asynchronously.
359
- The method will return the request thread.
360
- If parameter async_req is False or missing,
361
- then the method will return the response directly.
362
- """
363
- if not async_req:
364
- return self.__call_api(resource_path, method,
365
- path_params, query_params, header_params,
366
- body, post_params, files,
367
- response_type, auth_settings,
368
- _return_http_data_only, collection_formats,
369
- _preload_content, _request_timeout, _host)
370
-
371
- return self.pool.apply_async(self.__call_api, (resource_path,
372
- method, path_params,
373
- query_params,
374
- header_params, body,
375
- post_params, files,
376
- response_type,
377
- auth_settings,
378
- _return_http_data_only,
379
- collection_formats,
380
- _preload_content,
381
- _request_timeout,
382
- _host))
383
-
384
- def request(self, method, url, query_params=None, headers=None,
385
- post_params=None, body=None, _preload_content=True,
386
- _request_timeout=None):
387
- """Makes the HTTP request using RESTClient."""
388
- if method == "GET":
389
- return self.rest_client.GET(url,
390
- query_params=query_params,
391
- _preload_content=_preload_content,
392
- _request_timeout=_request_timeout,
393
- headers=headers)
394
- elif method == "HEAD":
395
- return self.rest_client.HEAD(url,
396
- query_params=query_params,
397
- _preload_content=_preload_content,
398
- _request_timeout=_request_timeout,
399
- headers=headers)
400
- elif method == "OPTIONS":
401
- return self.rest_client.OPTIONS(url,
402
- query_params=query_params,
403
- headers=headers,
404
- _preload_content=_preload_content,
405
- _request_timeout=_request_timeout)
406
- elif method == "POST":
407
- return self.rest_client.POST(url,
408
- query_params=query_params,
409
- headers=headers,
410
- post_params=post_params,
411
- _preload_content=_preload_content,
412
- _request_timeout=_request_timeout,
413
- body=body)
414
- elif method == "PUT":
415
- return self.rest_client.PUT(url,
416
- query_params=query_params,
417
- headers=headers,
418
- post_params=post_params,
419
- _preload_content=_preload_content,
420
- _request_timeout=_request_timeout,
421
- body=body)
422
- elif method == "PATCH":
423
- return self.rest_client.PATCH(url,
424
- query_params=query_params,
425
- headers=headers,
426
- post_params=post_params,
427
- _preload_content=_preload_content,
428
- _request_timeout=_request_timeout,
429
- body=body)
430
- elif method == "DELETE":
431
- return self.rest_client.DELETE(url,
432
- query_params=query_params,
433
- headers=headers,
434
- _preload_content=_preload_content,
435
- _request_timeout=_request_timeout,
436
- body=body)
437
- else:
438
- raise ApiValueError(
439
- "http method must be `GET`, `HEAD`, `OPTIONS`,"
440
- " `POST`, `PATCH`, `PUT` or `DELETE`."
441
- )
442
-
443
451
  def parameters_to_tuples(self, params, collection_formats):
444
452
  """Get parameters as list of tuples, formatting collections.
445
453
 
@@ -447,10 +455,10 @@ class ApiClient(object):
447
455
  :param dict collection_formats: Parameter collection formats
448
456
  :return: Parameters as list of tuples, collections formatted
449
457
  """
450
- new_params = []
458
+ new_params: List[Tuple[str, str]] = []
451
459
  if collection_formats is None:
452
460
  collection_formats = {}
453
- for k, v in six.iteritems(params) if isinstance(params, dict) else params: # noqa: E501
461
+ for k, v in params.items() if isinstance(params, dict) else params:
454
462
  if k in collection_formats:
455
463
  collection_format = collection_formats[k]
456
464
  if collection_format == 'multi':
@@ -470,45 +478,85 @@ class ApiClient(object):
470
478
  new_params.append((k, v))
471
479
  return new_params
472
480
 
473
- def files_parameters(self, files=None):
481
+ def parameters_to_url_query(self, params, collection_formats):
482
+ """Get parameters as list of tuples, formatting collections.
483
+
484
+ :param params: Parameters as dict or list of two-tuples
485
+ :param dict collection_formats: Parameter collection formats
486
+ :return: URL query string (e.g. a=Hello%20World&b=123)
487
+ """
488
+ new_params: List[Tuple[str, str]] = []
489
+ if collection_formats is None:
490
+ collection_formats = {}
491
+ for k, v in params.items() if isinstance(params, dict) else params:
492
+ if isinstance(v, bool):
493
+ v = str(v).lower()
494
+ if isinstance(v, (int, float)):
495
+ v = str(v)
496
+ if isinstance(v, dict):
497
+ v = json.dumps(v)
498
+
499
+ if k in collection_formats:
500
+ collection_format = collection_formats[k]
501
+ if collection_format == 'multi':
502
+ new_params.extend((k, str(value)) for value in v)
503
+ else:
504
+ if collection_format == 'ssv':
505
+ delimiter = ' '
506
+ elif collection_format == 'tsv':
507
+ delimiter = '\t'
508
+ elif collection_format == 'pipes':
509
+ delimiter = '|'
510
+ else: # csv is the default
511
+ delimiter = ','
512
+ new_params.append(
513
+ (k, delimiter.join(quote(str(value)) for value in v))
514
+ )
515
+ else:
516
+ new_params.append((k, quote(str(v))))
517
+
518
+ return "&".join(["=".join(map(str, item)) for item in new_params])
519
+
520
+ def files_parameters(self, files: Dict[str, Union[str, bytes]]):
474
521
  """Builds form parameters.
475
522
 
476
523
  :param files: File parameters.
477
524
  :return: Form parameters with files.
478
525
  """
479
526
  params = []
480
-
481
- if files:
482
- for k, v in six.iteritems(files):
483
- if not v:
484
- continue
485
- file_names = v if type(v) is list else [v]
486
- for n in file_names:
487
- with open(n, 'rb') as f:
488
- filename = os.path.basename(f.name)
489
- filedata = f.read()
490
- mimetype = (mimetypes.guess_type(filename)[0] or
491
- 'application/octet-stream')
492
- params.append(
493
- tuple([k, tuple([filename, filedata, mimetype])]))
494
-
527
+ for k, v in files.items():
528
+ if isinstance(v, str):
529
+ with open(v, 'rb') as f:
530
+ filename = os.path.basename(f.name)
531
+ filedata = f.read()
532
+ elif isinstance(v, bytes):
533
+ filename = k
534
+ filedata = v
535
+ else:
536
+ raise ValueError("Unsupported file value")
537
+ mimetype = (
538
+ mimetypes.guess_type(filename)[0]
539
+ or 'application/octet-stream'
540
+ )
541
+ params.append(
542
+ tuple([k, tuple([filename, filedata, mimetype])])
543
+ )
495
544
  return params
496
545
 
497
- def select_header_accept(self, accepts):
546
+ def select_header_accept(self, accepts: List[str]) -> Optional[str]:
498
547
  """Returns `Accept` based on an array of accepts provided.
499
548
 
500
549
  :param accepts: List of headers.
501
550
  :return: Accept (e.g. application/json).
502
551
  """
503
552
  if not accepts:
504
- return
553
+ return None
505
554
 
506
- accepts = [x.lower() for x in accepts]
555
+ for accept in accepts:
556
+ if re.search('json', accept, re.IGNORECASE):
557
+ return accept
507
558
 
508
- if 'application/json' in accepts:
509
- return 'application/json'
510
- else:
511
- return ', '.join(accepts)
559
+ return accepts[0]
512
560
 
513
561
  def select_header_content_type(self, content_types):
514
562
  """Returns `Content-Type` based on an array of content_types provided.
@@ -517,45 +565,101 @@ class ApiClient(object):
517
565
  :return: Content-Type (e.g. application/json).
518
566
  """
519
567
  if not content_types:
520
- return 'application/json'
521
-
522
- content_types = [x.lower() for x in content_types]
523
-
524
- if 'application/json' in content_types or '*/*' in content_types:
525
- return 'application/json'
526
- else:
527
- return content_types[0]
568
+ return None
528
569
 
529
- def update_params_for_auth(self, headers, querys, auth_settings):
570
+ for content_type in content_types:
571
+ if re.search('json', content_type, re.IGNORECASE):
572
+ return content_type
573
+
574
+ return content_types[0]
575
+
576
+ def update_params_for_auth(
577
+ self,
578
+ headers,
579
+ queries,
580
+ auth_settings,
581
+ resource_path,
582
+ method,
583
+ body,
584
+ request_auth=None
585
+ ) -> None:
530
586
  """Updates header and query params based on authentication setting.
531
587
 
532
588
  :param headers: Header parameters dict to be updated.
533
- :param querys: Query parameters tuple list to be updated.
589
+ :param queries: Query parameters tuple list to be updated.
534
590
  :param auth_settings: Authentication setting identifiers list.
591
+ :resource_path: A string representation of the HTTP request resource path.
592
+ :method: A string representation of the HTTP request method.
593
+ :body: A object representing the body of the HTTP request.
594
+ The object type is the return value of sanitize_for_serialization().
595
+ :param request_auth: if set, the provided settings will
596
+ override the token in the configuration.
535
597
  """
536
598
  if not auth_settings:
537
599
  return
538
600
 
539
- for auth in auth_settings:
540
- auth_setting = self.configuration.auth_settings().get(auth)
541
- if auth_setting:
542
- if auth_setting['in'] == 'cookie':
543
- headers['Cookie'] = auth_setting['value']
544
- elif auth_setting['in'] == 'header':
545
- headers[auth_setting['key']] = auth_setting['value']
546
- elif auth_setting['in'] == 'query':
547
- querys.append((auth_setting['key'], auth_setting['value']))
548
- else:
549
- raise ApiValueError(
550
- 'Authentication token must be in `query` or `header`'
601
+ if request_auth:
602
+ self._apply_auth_params(
603
+ headers,
604
+ queries,
605
+ resource_path,
606
+ method,
607
+ body,
608
+ request_auth
609
+ )
610
+ else:
611
+ for auth in auth_settings:
612
+ auth_setting = self.configuration.auth_settings().get(auth)
613
+ if auth_setting:
614
+ self._apply_auth_params(
615
+ headers,
616
+ queries,
617
+ resource_path,
618
+ method,
619
+ body,
620
+ auth_setting
551
621
  )
552
622
 
623
+ def _apply_auth_params(
624
+ self,
625
+ headers,
626
+ queries,
627
+ resource_path,
628
+ method,
629
+ body,
630
+ auth_setting
631
+ ) -> None:
632
+ """Updates the request parameters based on a single auth_setting
633
+
634
+ :param headers: Header parameters dict to be updated.
635
+ :param queries: Query parameters tuple list to be updated.
636
+ :resource_path: A string representation of the HTTP request resource path.
637
+ :method: A string representation of the HTTP request method.
638
+ :body: A object representing the body of the HTTP request.
639
+ The object type is the return value of sanitize_for_serialization().
640
+ :param auth_setting: auth settings for the endpoint
641
+ """
642
+ if auth_setting['in'] == 'cookie':
643
+ headers['Cookie'] = auth_setting['value']
644
+ elif auth_setting['in'] == 'header':
645
+ if auth_setting['type'] != 'http-signature':
646
+ headers[auth_setting['key']] = auth_setting['value']
647
+ elif auth_setting['in'] == 'query':
648
+ queries.append((auth_setting['key'], auth_setting['value']))
649
+ else:
650
+ raise ApiValueError(
651
+ 'Authentication token must be in `query` or `header`'
652
+ )
653
+
553
654
  def __deserialize_file(self, response):
554
655
  """Deserializes body to file
555
656
 
556
657
  Saves response body into a file in a temporary folder,
557
658
  using the filename from the `Content-Disposition` header if provided.
558
659
 
660
+ handle file downloading
661
+ save response body into a tmp file and return the instance
662
+
559
663
  :param response: RESTResponse.
560
664
  :return: file path.
561
665
  """
@@ -565,8 +669,12 @@ class ApiClient(object):
565
669
 
566
670
  content_disposition = response.getheader("Content-Disposition")
567
671
  if content_disposition:
568
- filename = re.search(r'filename=[\'"]?([^\'"\s]+)[\'"]?',
569
- content_disposition).group(1)
672
+ m = re.search(
673
+ r'filename=[\'"]?([^\'"\s]+)[\'"]?',
674
+ content_disposition
675
+ )
676
+ assert m is not None, "Unexpected 'content-disposition' header value"
677
+ filename = m.group(1)
570
678
  path = os.path.join(os.path.dirname(path), filename)
571
679
 
572
680
  with open(path, "wb") as f:
@@ -585,7 +693,7 @@ class ApiClient(object):
585
693
  try:
586
694
  return klass(data)
587
695
  except UnicodeEncodeError:
588
- return six.text_type(data)
696
+ return str(data)
589
697
  except TypeError:
590
698
  return data
591
699
 
@@ -633,6 +741,24 @@ class ApiClient(object):
633
741
  )
634
742
  )
635
743
 
744
+ def __deserialize_enum(self, data, klass):
745
+ """Deserializes primitive type to enum.
746
+
747
+ :param data: primitive type.
748
+ :param klass: class literal.
749
+ :return: enum value.
750
+ """
751
+ try:
752
+ return klass(data)
753
+ except ValueError:
754
+ raise rest.ApiException(
755
+ status=0,
756
+ reason=(
757
+ "Failed to parse `{0}` as `{1}`"
758
+ .format(data, klass)
759
+ )
760
+ )
761
+
636
762
  def __deserialize_model(self, data, klass):
637
763
  """Deserializes list or dict to model.
638
764
 
@@ -640,27 +766,5 @@ class ApiClient(object):
640
766
  :param klass: class literal.
641
767
  :return: model object.
642
768
  """
643
- has_discriminator = False
644
- if (hasattr(klass, 'get_real_child_model')
645
- and klass.discriminator_value_class_map):
646
- has_discriminator = True
647
-
648
- if not klass.openapi_types and has_discriminator is False:
649
- return data
650
769
 
651
- kwargs = {}
652
- if (data is not None and
653
- klass.openapi_types is not None and
654
- isinstance(data, (list, dict))):
655
- for attr, attr_type in six.iteritems(klass.openapi_types):
656
- if klass.attribute_map[attr] in data:
657
- value = data[klass.attribute_map[attr]]
658
- kwargs[attr] = self.__deserialize(value, attr_type)
659
-
660
- instance = klass(**kwargs)
661
-
662
- if has_discriminator:
663
- klass_name = instance.get_real_child_model(data)
664
- if klass_name:
665
- instance = self.__deserialize(data, klass_name)
666
- return instance
770
+ return klass.from_dict(data)