Skip to content

Commit 5afd0ab

Browse files
therveci.datadog-api-specamaskara-dd
authored
Refactor data serialization (#2211)
* Refactor data serialization Remove classmethod sanitize_for_serialization, and fix model_to_dict to use the same logic so that it handles list of list properly for example. * pre-commit fixes * pre-commit fixes --------- Co-authored-by: ci.datadog-api-spec <[email protected]> Co-authored-by: amaskara-dd <[email protected]>
1 parent bd3200c commit 5afd0ab

12 files changed

+5441
-5458
lines changed

.generator/src/generator/templates/api_client.j2

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import mimetypes
66
import warnings
77
import multiprocessing
88
from multiprocessing.pool import ThreadPool
9-
from datetime import date, datetime
10-
from uuid import UUID
119
import io
1210
import os
1311
import re
@@ -21,14 +19,12 @@ from {{ package }} import rest
2119
from {{ package }}.configuration import Configuration
2220
from {{ package }}.exceptions import ApiTypeError, ApiValueError
2321
from {{ package }}.model_utils import (
24-
ModelNormal,
25-
ModelSimple,
26-
ModelComposed,
2722
check_allowed_values,
2823
check_validations,
2924
deserialize_file,
3025
file_type,
31-
model_to_dict,
26+
data_to_dict,
27+
get_file_data_and_close_file,
3228
validate_and_convert_types,
3329
get_attribute_from_path,
3430
set_attribute_from_path,
@@ -154,40 +150,6 @@ class ApiClient:
154150
new_params.append((k, v))
155151
return new_params
156152

157-
@classmethod
158-
def sanitize_for_serialization(cls, obj):
159-
"""Prepares data for transmission before it is sent with the rest client.
160-
If obj is None, return None.
161-
If obj is str, int, long, float, bool, return directly.
162-
If obj is datetime.datetime, datetime.date convert to string in iso8601 format.
163-
If obj is list, sanitize each element in the list.
164-
If obj is dict, return the dict.
165-
If obj is OpenAPI model, return the properties dict.
166-
If obj is io.IOBase, return the bytes.
167-
168-
:param obj: The data to serialize.
169-
:return: The serialized form of data.
170-
"""
171-
if isinstance(obj, (ModelNormal, ModelComposed)):
172-
return {key: cls.sanitize_for_serialization(val) for key, val in model_to_dict(obj).items()}
173-
elif isinstance(obj, io.IOBase):
174-
return cls.get_file_data_and_close_file(obj)
175-
elif isinstance(obj, (str, int, float, bool)) or obj is None:
176-
return obj
177-
elif isinstance(obj, (datetime, date)):
178-
if getattr(obj, "tzinfo", None) is not None:
179-
return obj.isoformat()
180-
return "{}Z".format(obj.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3])
181-
elif isinstance(obj, UUID ):
182-
return str(obj)
183-
elif isinstance(obj, ModelSimple):
184-
return cls.sanitize_for_serialization(obj.value)
185-
elif isinstance(obj, (list, tuple)):
186-
return [cls.sanitize_for_serialization(item) for item in obj]
187-
if isinstance(obj, dict):
188-
return {key: cls.sanitize_for_serialization(val) for key, val in obj.items()}
189-
raise ApiValueError("Unable to prepare type {} for serialization".format(obj.__class__.__name__))
190-
191153
def deserialize(self, response_data: str, response_type: Any, check_type: Optional[bool]):
192154
"""Deserializes response into an object.
193155

@@ -286,12 +248,12 @@ class ApiClient:
286248
header_params = header_params or {}
287249
header_params.update(self.default_headers)
288250
if header_params:
289-
header_params = self.sanitize_for_serialization(header_params)
251+
header_params = data_to_dict(header_params)
290252
header_params = dict(self.parameters_to_tuples(header_params, collection_formats))
291253

292254
# path parameters
293255
if path_params:
294-
path_params = self.sanitize_for_serialization(path_params)
256+
path_params = data_to_dict(path_params)
295257
for k, v in self.parameters_to_tuples(path_params, collection_formats):
296258
# specified safe chars, encode everything
297259
resource_path = resource_path.replace(
@@ -302,21 +264,21 @@ class ApiClient:
302264

303265
# query parameters
304266
if query_params:
305-
query_params = self.sanitize_for_serialization(query_params)
267+
query_params = data_to_dict(query_params)
306268
query_params = self.parameters_to_tuples(query_params, collection_formats)
307269

308270
# post parameters
309271
if post_params or files:
310272
post_params = post_params or []
311-
post_params = self.sanitize_for_serialization(post_params)
273+
post_params = data_to_dict(post_params)
312274
post_params = self.parameters_to_tuples(post_params, collection_formats)
313275
post_params.extend(self.files_parameters(files))
314276
if header_params["Content-Type"].startswith("multipart"):
315277
post_params = self.parameters_to_multipart(post_params)
316278

317279
# body
318280
if body:
319-
body = self.sanitize_for_serialization(body)
281+
body = data_to_dict(body)
320282

321283
# request url
322284
if host is None:
@@ -439,12 +401,6 @@ class ApiClient:
439401
new_params.append((k, v))
440402
return new_params
441403

442-
@staticmethod
443-
def get_file_data_and_close_file(file_instance: io.IOBase) -> bytes:
444-
file_data = file_instance.read()
445-
file_instance.close()
446-
return file_data
447-
448404
def files_parameters(self, files: Optional[Dict[str, List[io.FileIO]]] = None):
449405
"""Builds form parameters.
450406

@@ -469,7 +425,7 @@ class ApiClient:
469425
"Cannot read a closed file. The passed in file_type " "for %s must be open." % param_name
470426
)
471427
filename = os.path.basename(str(file_instance.name))
472-
filedata = self.get_file_data_and_close_file(file_instance)
428+
filedata = get_file_data_and_close_file(file_instance)
473429
mimetype = mimetypes.guess_type(filename)[0] or "application/octet-stream"
474430
params.append(tuple([param_name, tuple([filename, filedata, mimetype])]))
475431

.generator/src/generator/templates/model_utils.j2

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,53 @@ def validate_and_convert_types(
13621362
return input_value
13631363

13641364

1365+
def get_file_data_and_close_file(file_instance: io.IOBase) -> bytes:
1366+
file_data = file_instance.read()
1367+
file_instance.close()
1368+
return file_data
1369+
1370+
1371+
def data_to_dict(instance, serialize=True):
1372+
"""Prepares data for transmission before it is sent with the rest client.
1373+
1374+
If obj is None, return None.
1375+
If obj is str, int, long, float, bool, return directly.
1376+
If obj is datetime.datetime, datetime.date convert to string in iso8601 format.
1377+
If obj is list, sanitize each element in the list.
1378+
If obj is dict, return the dict.
1379+
If obj is OpenAPI model, return the properties dict.
1380+
If obj is io.IOBase, return the bytes.
1381+
1382+
:param obj: The data to serialize.
1383+
:param serialize: If True, return data safe for wire. Forwarded to model_to_dict.
1384+
:type serialize: bool
1385+
:return: The serialized form of data.
1386+
"""
1387+
if isinstance(instance, (ModelNormal, ModelComposed)):
1388+
return {key: data_to_dict(val) for key, val in model_to_dict(instance, serialize).items()}
1389+
elif isinstance(instance, io.IOBase):
1390+
return get_file_data_and_close_file(instance)
1391+
elif isinstance(instance, (str, int, float, bool)) or instance is None:
1392+
return instance
1393+
elif isinstance(instance, (datetime, date)):
1394+
if not serialize:
1395+
return instance
1396+
if getattr(instance, "tzinfo", None) is not None:
1397+
return instance.isoformat()
1398+
return "{}Z".format(instance.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3])
1399+
elif isinstance(instance, UUID):
1400+
if not serialize:
1401+
return instance
1402+
return str(instance)
1403+
elif isinstance(instance, ModelSimple):
1404+
return data_to_dict(instance.value)
1405+
elif isinstance(instance, (list, tuple)):
1406+
return [data_to_dict(item) for item in instance]
1407+
if isinstance(instance, dict):
1408+
return {key: data_to_dict(val) for key, val in instance.items()}
1409+
raise ApiValueError("Unable to handle type {}".format(instance.__class__.__name__))
1410+
1411+
13651412
def model_to_dict(model_instance, serialize=True):
13661413
"""Returns the model properties as a dict.
13671414

@@ -1392,37 +1439,7 @@ def model_to_dict(model_instance, serialize=True):
13921439
seen_json_attribute_names.add(attr)
13931440
except KeyError:
13941441
used_fallback_python_attribute_names.add(attr)
1395-
if isinstance(value, list):
1396-
if not value:
1397-
# empty list or None
1398-
result[attr] = value
1399-
else:
1400-
res = []
1401-
for v in value:
1402-
if isinstance(v, PRIMITIVE_TYPES) or v is None:
1403-
res.append(v)
1404-
elif isinstance(v, ModelSimple):
1405-
res.append(v.value)
1406-
elif isinstance(v, OpenApiModel):
1407-
res.append(model_to_dict(v, serialize=serialize))
1408-
else:
1409-
res.append(v)
1410-
result[attr] = res
1411-
elif isinstance(value, dict):
1412-
result[attr] = dict(
1413-
map(
1414-
lambda item: (item[0], model_to_dict(item[1], serialize=serialize))
1415-
if hasattr(item[1], "_data_store")
1416-
else item,
1417-
value.items(),
1418-
)
1419-
)
1420-
elif isinstance(value, ModelSimple):
1421-
result[attr] = value.value
1422-
elif hasattr(value, "_data_store"):
1423-
result[attr] = model_to_dict(value, serialize=serialize)
1424-
else:
1425-
result[attr] = value
1442+
result[attr] = data_to_dict(value, serialize)
14261443
if serialize:
14271444
for python_key in used_fallback_python_attribute_names:
14281445
json_key = py_to_json_map.get(python_key)

docs/datadog_api_client.rst

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,55 @@
1-
datadog\_api\_client
2-
====================
1+
datadog\_api\_client package
2+
============================
3+
4+
Subpackages
5+
-----------
36

47
.. toctree::
58
:maxdepth: 4
69

710
datadog_api_client.v1
811
datadog_api_client.v2
912

13+
Submodules
14+
----------
1015

11-
api\_client
12-
-----------
16+
datadog\_api\_client.api\_client module
17+
---------------------------------------
1318

1419
.. automodule:: datadog_api_client.api_client
1520
:members:
1621
:show-inheritance:
1722

18-
configuration
19-
-------------
23+
datadog\_api\_client.configuration module
24+
-----------------------------------------
2025

2126
.. automodule:: datadog_api_client.configuration
2227
:members:
2328
:show-inheritance:
2429

25-
exceptions
26-
----------
30+
datadog\_api\_client.exceptions module
31+
--------------------------------------
2732

2833
.. automodule:: datadog_api_client.exceptions
2934
:members:
3035
:show-inheritance:
3136

32-
model\_utils
33-
------------
37+
datadog\_api\_client.model\_utils module
38+
----------------------------------------
3439

3540
.. automodule:: datadog_api_client.model_utils
3641
:members:
3742
:show-inheritance:
3843

39-
rest
40-
----
44+
datadog\_api\_client.rest module
45+
--------------------------------
4146

4247
.. automodule:: datadog_api_client.rest
4348
:members:
4449
:show-inheritance:
4550

51+
Module contents
52+
---------------
4653

4754
.. automodule:: datadog_api_client
4855
:members:

0 commit comments

Comments
 (0)