plex-api-client 0.6.1__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.
- plex_api/__init__.py +4 -0
- plex_api/_hooks/__init__.py +5 -0
- plex_api/_hooks/registration.py +13 -0
- plex_api/_hooks/sdkhooks.py +57 -0
- plex_api/_hooks/types.py +74 -0
- plex_api/activities.py +151 -0
- plex_api/authentication.py +146 -0
- plex_api/butler.py +333 -0
- plex_api/hubs.py +158 -0
- plex_api/library.py +808 -0
- plex_api/log.py +225 -0
- plex_api/media.py +210 -0
- plex_api/models/__init__.py +4 -0
- plex_api/models/components/__init__.py +5 -0
- plex_api/models/components/security.py +11 -0
- plex_api/models/errors/__init__.py +63 -0
- plex_api/models/errors/addplaylistcontents.py +32 -0
- plex_api/models/errors/applyupdates.py +32 -0
- plex_api/models/errors/cancelserveractivities.py +32 -0
- plex_api/models/errors/checkforupdates.py +32 -0
- plex_api/models/errors/clearplaylistcontents.py +32 -0
- plex_api/models/errors/createplaylist.py +32 -0
- plex_api/models/errors/deletelibrary.py +32 -0
- plex_api/models/errors/deleteplaylist.py +32 -0
- plex_api/models/errors/enablepapertrail.py +32 -0
- plex_api/models/errors/getavailableclients.py +32 -0
- plex_api/models/errors/getbutlertasks.py +32 -0
- plex_api/models/errors/getdevices.py +32 -0
- plex_api/models/errors/getfilehash.py +32 -0
- plex_api/models/errors/getglobalhubs.py +32 -0
- plex_api/models/errors/getlibraries.py +32 -0
- plex_api/models/errors/getlibrary.py +32 -0
- plex_api/models/errors/getlibraryhubs.py +32 -0
- plex_api/models/errors/getmetadata.py +32 -0
- plex_api/models/errors/getmetadatachildren.py +32 -0
- plex_api/models/errors/getmyplexaccount.py +32 -0
- plex_api/models/errors/getondeck.py +32 -0
- plex_api/models/errors/getpin.py +32 -0
- plex_api/models/errors/getplaylist.py +32 -0
- plex_api/models/errors/getplaylistcontents.py +32 -0
- plex_api/models/errors/getplaylists.py +32 -0
- plex_api/models/errors/getrecentlyadded.py +32 -0
- plex_api/models/errors/getresizedphoto.py +32 -0
- plex_api/models/errors/getsearchresults.py +32 -0
- plex_api/models/errors/getserveractivities.py +32 -0
- plex_api/models/errors/getservercapabilities.py +32 -0
- plex_api/models/errors/getserveridentity.py +32 -0
- plex_api/models/errors/getserverlist.py +32 -0
- plex_api/models/errors/getserverpreferences.py +32 -0
- plex_api/models/errors/getsessionhistory.py +32 -0
- plex_api/models/errors/getsessions.py +32 -0
- plex_api/models/errors/getsourceconnectioninformation.py +32 -0
- plex_api/models/errors/getstatistics.py +32 -0
- plex_api/models/errors/gettimeline.py +32 -0
- plex_api/models/errors/gettoken.py +32 -0
- plex_api/models/errors/gettranscodesessions.py +32 -0
- plex_api/models/errors/gettransienttoken.py +32 -0
- plex_api/models/errors/getupdatestatus.py +32 -0
- plex_api/models/errors/logline.py +32 -0
- plex_api/models/errors/logmultiline.py +32 -0
- plex_api/models/errors/markplayed.py +32 -0
- plex_api/models/errors/markunplayed.py +32 -0
- plex_api/models/errors/performsearch.py +32 -0
- plex_api/models/errors/performvoicesearch.py +32 -0
- plex_api/models/errors/refreshlibrary.py +32 -0
- plex_api/models/errors/sdkerror.py +24 -0
- plex_api/models/errors/startalltasks.py +32 -0
- plex_api/models/errors/starttask.py +32 -0
- plex_api/models/errors/startuniversaltranscode.py +32 -0
- plex_api/models/errors/stopalltasks.py +32 -0
- plex_api/models/errors/stoptask.py +32 -0
- plex_api/models/errors/stoptranscodesession.py +32 -0
- plex_api/models/errors/updateplaylist.py +32 -0
- plex_api/models/errors/updateplayprogress.py +32 -0
- plex_api/models/errors/uploadplaylist.py +32 -0
- plex_api/models/operations/__init__.py +64 -0
- plex_api/models/operations/addplaylistcontents.py +74 -0
- plex_api/models/operations/applyupdates.py +39 -0
- plex_api/models/operations/cancelserveractivities.py +25 -0
- plex_api/models/operations/checkforupdates.py +32 -0
- plex_api/models/operations/clearplaylistcontents.py +25 -0
- plex_api/models/operations/createplaylist.py +91 -0
- plex_api/models/operations/deletelibrary.py +25 -0
- plex_api/models/operations/deleteplaylist.py +25 -0
- plex_api/models/operations/enablepapertrail.py +17 -0
- plex_api/models/operations/getavailableclients.py +58 -0
- plex_api/models/operations/getbutlertasks.py +52 -0
- plex_api/models/operations/getdevices.py +53 -0
- plex_api/models/operations/getfilehash.py +28 -0
- plex_api/models/operations/getglobalhubs.py +100 -0
- plex_api/models/operations/getlibraries.py +79 -0
- plex_api/models/operations/getlibrary.py +153 -0
- plex_api/models/operations/getlibraryhubs.py +205 -0
- plex_api/models/operations/getlibraryitems.py +227 -0
- plex_api/models/operations/getmetadata.py +264 -0
- plex_api/models/operations/getmetadatachildren.py +115 -0
- plex_api/models/operations/getmyplexaccount.py +50 -0
- plex_api/models/operations/getondeck.py +166 -0
- plex_api/models/operations/getpin.py +86 -0
- plex_api/models/operations/getplaylist.py +70 -0
- plex_api/models/operations/getplaylistcontents.py +178 -0
- plex_api/models/operations/getplaylists.py +85 -0
- plex_api/models/operations/getrecentlyadded.py +166 -0
- plex_api/models/operations/getresizedphoto.py +48 -0
- plex_api/models/operations/getsearchresults.py +182 -0
- plex_api/models/operations/getserveractivities.py +63 -0
- plex_api/models/operations/getservercapabilities.py +99 -0
- plex_api/models/operations/getserveridentity.py +42 -0
- plex_api/models/operations/getserverlist.py +53 -0
- plex_api/models/operations/getserverpreferences.py +57 -0
- plex_api/models/operations/getsessionhistory.py +67 -0
- plex_api/models/operations/getsessions.py +185 -0
- plex_api/models/operations/getsourceconnectioninformation.py +25 -0
- plex_api/models/operations/getstatistics.py +93 -0
- plex_api/models/operations/gettimeline.py +50 -0
- plex_api/models/operations/gettoken.py +35 -0
- plex_api/models/operations/gettranscodesessions.py +71 -0
- plex_api/models/operations/gettransienttoken.py +36 -0
- plex_api/models/operations/getupdatestatus.py +57 -0
- plex_api/models/operations/logline.py +50 -0
- plex_api/models/operations/logmultiline.py +17 -0
- plex_api/models/operations/markplayed.py +25 -0
- plex_api/models/operations/markunplayed.py +25 -0
- plex_api/models/operations/performsearch.py +30 -0
- plex_api/models/operations/performvoicesearch.py +30 -0
- plex_api/models/operations/refreshlibrary.py +25 -0
- plex_api/models/operations/searchlibrary.py +96 -0
- plex_api/models/operations/startalltasks.py +17 -0
- plex_api/models/operations/starttask.py +43 -0
- plex_api/models/operations/startuniversaltranscode.py +56 -0
- plex_api/models/operations/stopalltasks.py +17 -0
- plex_api/models/operations/stoptask.py +43 -0
- plex_api/models/operations/stoptranscodesession.py +25 -0
- plex_api/models/operations/updateplaylist.py +30 -0
- plex_api/models/operations/updateplayprogress.py +29 -0
- plex_api/models/operations/uploadplaylist.py +47 -0
- plex_api/playlists.py +629 -0
- plex_api/plex.py +154 -0
- plex_api/sdk.py +180 -0
- plex_api/sdkconfiguration.py +52 -0
- plex_api/search.py +232 -0
- plex_api/server.py +525 -0
- plex_api/sessions.py +272 -0
- plex_api/statistics.py +86 -0
- plex_api/updater.py +212 -0
- plex_api/utils/__init__.py +4 -0
- plex_api/utils/retries.py +120 -0
- plex_api/utils/utils.py +898 -0
- plex_api/video.py +136 -0
- plex_api_client-0.6.1.dist-info/LICENSE.md +21 -0
- plex_api_client-0.6.1.dist-info/METADATA +397 -0
- plex_api_client-0.6.1.dist-info/RECORD +154 -0
- plex_api_client-0.6.1.dist-info/WHEEL +5 -0
- plex_api_client-0.6.1.dist-info/top_level.txt +1 -0
plex_api/utils/utils.py
ADDED
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
"""Code generated by Speakeasy (https://speakeasyapi.dev). DO NOT EDIT."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
import sys
|
|
7
|
+
from dataclasses import Field, fields, is_dataclass, make_dataclass
|
|
8
|
+
from datetime import date, datetime
|
|
9
|
+
from decimal import Decimal
|
|
10
|
+
from email.message import Message
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from typing import (Any, Callable, Dict, List, Optional, Tuple, Union,
|
|
13
|
+
get_args, get_origin)
|
|
14
|
+
from xmlrpc.client import boolean
|
|
15
|
+
from typing_inspect import is_optional_type
|
|
16
|
+
import dateutil.parser
|
|
17
|
+
from dataclasses_json import DataClassJsonMixin
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_security(security: Any) -> Tuple[Dict[str, str], Dict[str, str]]:
|
|
21
|
+
headers: Dict[str, str] = {}
|
|
22
|
+
query_params: Dict[str, str] = {}
|
|
23
|
+
|
|
24
|
+
if security is None:
|
|
25
|
+
return headers, query_params
|
|
26
|
+
|
|
27
|
+
sec_fields: Tuple[Field, ...] = fields(security)
|
|
28
|
+
for sec_field in sec_fields:
|
|
29
|
+
value = getattr(security, sec_field.name)
|
|
30
|
+
if value is None:
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
metadata = sec_field.metadata.get('security')
|
|
34
|
+
if metadata is None:
|
|
35
|
+
continue
|
|
36
|
+
if metadata.get('option'):
|
|
37
|
+
_parse_security_option(headers, query_params, value)
|
|
38
|
+
return headers, query_params
|
|
39
|
+
if metadata.get('scheme'):
|
|
40
|
+
# Special case for basic auth which could be a flattened struct
|
|
41
|
+
if metadata.get("sub_type") == "basic" and not is_dataclass(value):
|
|
42
|
+
_parse_security_scheme(headers, query_params, metadata, security)
|
|
43
|
+
else:
|
|
44
|
+
_parse_security_scheme(headers, query_params, metadata, value)
|
|
45
|
+
|
|
46
|
+
return headers, query_params
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _parse_security_option(headers: Dict[str, str], query_params: Dict[str, str], option: Any):
|
|
50
|
+
opt_fields: Tuple[Field, ...] = fields(option)
|
|
51
|
+
for opt_field in opt_fields:
|
|
52
|
+
metadata = opt_field.metadata.get('security')
|
|
53
|
+
if metadata is None or metadata.get('scheme') is None:
|
|
54
|
+
continue
|
|
55
|
+
_parse_security_scheme(
|
|
56
|
+
headers, query_params, metadata, getattr(option, opt_field.name))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _parse_security_scheme(headers: Dict[str, str], query_params: Dict[str, str], scheme_metadata: Dict, scheme: Any):
|
|
60
|
+
scheme_type = scheme_metadata.get('type')
|
|
61
|
+
sub_type = scheme_metadata.get('sub_type')
|
|
62
|
+
|
|
63
|
+
if is_dataclass(scheme):
|
|
64
|
+
if scheme_type == 'http' and sub_type == 'basic':
|
|
65
|
+
_parse_basic_auth_scheme(headers, scheme)
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
scheme_fields: Tuple[Field, ...] = fields(scheme)
|
|
69
|
+
for scheme_field in scheme_fields:
|
|
70
|
+
metadata = scheme_field.metadata.get('security')
|
|
71
|
+
if metadata is None or metadata.get('field_name') is None:
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
value = getattr(scheme, scheme_field.name)
|
|
75
|
+
|
|
76
|
+
_parse_security_scheme_value(
|
|
77
|
+
headers, query_params, scheme_metadata, metadata, value)
|
|
78
|
+
else:
|
|
79
|
+
_parse_security_scheme_value(
|
|
80
|
+
headers, query_params, scheme_metadata, scheme_metadata, scheme)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _parse_security_scheme_value(headers: Dict[str, str], query_params: Dict[str, str], scheme_metadata: Dict, security_metadata: Dict, value: Any):
|
|
84
|
+
scheme_type = scheme_metadata.get('type')
|
|
85
|
+
sub_type = scheme_metadata.get('sub_type')
|
|
86
|
+
|
|
87
|
+
header_name = str(security_metadata.get('field_name'))
|
|
88
|
+
|
|
89
|
+
if scheme_type == "apiKey":
|
|
90
|
+
if sub_type == 'header':
|
|
91
|
+
headers[header_name] = value
|
|
92
|
+
elif sub_type == 'query':
|
|
93
|
+
query_params[header_name] = value
|
|
94
|
+
else:
|
|
95
|
+
raise Exception('not supported')
|
|
96
|
+
elif scheme_type == "openIdConnect":
|
|
97
|
+
headers[header_name] = _apply_bearer(value)
|
|
98
|
+
elif scheme_type == 'oauth2':
|
|
99
|
+
if sub_type != 'client_credentials':
|
|
100
|
+
headers[header_name] = _apply_bearer(value)
|
|
101
|
+
elif scheme_type == 'http':
|
|
102
|
+
if sub_type == 'bearer':
|
|
103
|
+
headers[header_name] = _apply_bearer(value)
|
|
104
|
+
else:
|
|
105
|
+
raise Exception('not supported')
|
|
106
|
+
else:
|
|
107
|
+
raise Exception('not supported')
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _apply_bearer(token: str) -> str:
|
|
111
|
+
return token.lower().startswith('bearer ') and token or f'Bearer {token}'
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _parse_basic_auth_scheme(headers: Dict[str, str], scheme: Any):
|
|
115
|
+
username = ""
|
|
116
|
+
password = ""
|
|
117
|
+
|
|
118
|
+
scheme_fields: Tuple[Field, ...] = fields(scheme)
|
|
119
|
+
for scheme_field in scheme_fields:
|
|
120
|
+
metadata = scheme_field.metadata.get('security')
|
|
121
|
+
if metadata is None or metadata.get('field_name') is None:
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
field_name = metadata.get('field_name')
|
|
125
|
+
value = getattr(scheme, scheme_field.name)
|
|
126
|
+
|
|
127
|
+
if field_name == 'username':
|
|
128
|
+
username = value
|
|
129
|
+
if field_name == 'password':
|
|
130
|
+
password = value
|
|
131
|
+
|
|
132
|
+
data = f'{username}:{password}'.encode()
|
|
133
|
+
headers['Authorization'] = f'Basic {base64.b64encode(data).decode()}'
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def generate_url(clazz: type, server_url: str, path: str, path_params: Any,
|
|
137
|
+
gbls: Optional[Dict[str, Dict[str, Dict[str, Any]]]] = None) -> str:
|
|
138
|
+
path_param_fields: Tuple[Field, ...] = fields(clazz)
|
|
139
|
+
for field in path_param_fields:
|
|
140
|
+
request_metadata = field.metadata.get('request')
|
|
141
|
+
if request_metadata is not None:
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
param_metadata = field.metadata.get('path_param')
|
|
145
|
+
if param_metadata is None:
|
|
146
|
+
continue
|
|
147
|
+
|
|
148
|
+
param = getattr(
|
|
149
|
+
path_params, field.name) if path_params is not None else None
|
|
150
|
+
param = _populate_from_globals(
|
|
151
|
+
field.name, param, 'pathParam', gbls)
|
|
152
|
+
|
|
153
|
+
if param is None:
|
|
154
|
+
continue
|
|
155
|
+
|
|
156
|
+
f_name = param_metadata.get("field_name", field.name)
|
|
157
|
+
serialization = param_metadata.get('serialization', '')
|
|
158
|
+
if serialization != '':
|
|
159
|
+
serialized_params = _get_serialized_params(
|
|
160
|
+
param_metadata, field.type, f_name, param)
|
|
161
|
+
for key, value in serialized_params.items():
|
|
162
|
+
path = path.replace(
|
|
163
|
+
'{' + key + '}', value, 1)
|
|
164
|
+
else:
|
|
165
|
+
if param_metadata.get('style', 'simple') == 'simple':
|
|
166
|
+
if isinstance(param, List):
|
|
167
|
+
pp_vals: List[str] = []
|
|
168
|
+
for pp_val in param:
|
|
169
|
+
if pp_val is None:
|
|
170
|
+
continue
|
|
171
|
+
pp_vals.append(_val_to_string(pp_val))
|
|
172
|
+
path = path.replace(
|
|
173
|
+
'{' + param_metadata.get('field_name', field.name) + '}', ",".join(pp_vals), 1)
|
|
174
|
+
elif isinstance(param, Dict):
|
|
175
|
+
pp_vals: List[str] = []
|
|
176
|
+
for pp_key in param:
|
|
177
|
+
if param[pp_key] is None:
|
|
178
|
+
continue
|
|
179
|
+
if param_metadata.get('explode'):
|
|
180
|
+
pp_vals.append(
|
|
181
|
+
f"{pp_key}={_val_to_string(param[pp_key])}")
|
|
182
|
+
else:
|
|
183
|
+
pp_vals.append(
|
|
184
|
+
f"{pp_key},{_val_to_string(param[pp_key])}")
|
|
185
|
+
path = path.replace(
|
|
186
|
+
'{' + param_metadata.get('field_name', field.name) + '}', ",".join(pp_vals), 1)
|
|
187
|
+
elif not isinstance(param, (str, int, float, complex, bool, Decimal)):
|
|
188
|
+
pp_vals: List[str] = []
|
|
189
|
+
param_fields: Tuple[Field, ...] = fields(param)
|
|
190
|
+
for param_field in param_fields:
|
|
191
|
+
param_value_metadata = param_field.metadata.get(
|
|
192
|
+
'path_param')
|
|
193
|
+
if not param_value_metadata:
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
parm_name = param_value_metadata.get(
|
|
197
|
+
'field_name', field.name)
|
|
198
|
+
|
|
199
|
+
param_field_val = getattr(param, param_field.name)
|
|
200
|
+
if param_field_val is None:
|
|
201
|
+
continue
|
|
202
|
+
if param_metadata.get('explode'):
|
|
203
|
+
pp_vals.append(
|
|
204
|
+
f"{parm_name}={_val_to_string(param_field_val)}")
|
|
205
|
+
else:
|
|
206
|
+
pp_vals.append(
|
|
207
|
+
f"{parm_name},{_val_to_string(param_field_val)}")
|
|
208
|
+
path = path.replace(
|
|
209
|
+
'{' + param_metadata.get('field_name', field.name) + '}', ",".join(pp_vals), 1)
|
|
210
|
+
else:
|
|
211
|
+
path = path.replace(
|
|
212
|
+
'{' + param_metadata.get('field_name', field.name) + '}', _val_to_string(param), 1)
|
|
213
|
+
|
|
214
|
+
return remove_suffix(server_url, '/') + path
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def is_optional(field):
|
|
218
|
+
return get_origin(field) is Union and type(None) in get_args(field)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def template_url(url_with_params: str, params: Dict[str, str]) -> str:
|
|
222
|
+
for key, value in params.items():
|
|
223
|
+
url_with_params = url_with_params.replace(
|
|
224
|
+
'{' + key + '}', value)
|
|
225
|
+
|
|
226
|
+
return url_with_params
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def get_query_params(clazz: type, query_params: Any, gbls: Optional[Dict[str, Dict[str, Dict[str, Any]]]] = None) -> Dict[
|
|
230
|
+
str, List[str]]:
|
|
231
|
+
params: Dict[str, List[str]] = {}
|
|
232
|
+
|
|
233
|
+
param_fields: Tuple[Field, ...] = fields(clazz)
|
|
234
|
+
for field in param_fields:
|
|
235
|
+
request_metadata = field.metadata.get('request')
|
|
236
|
+
if request_metadata is not None:
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
metadata = field.metadata.get('query_param')
|
|
240
|
+
if not metadata:
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
param_name = field.name
|
|
244
|
+
value = getattr(
|
|
245
|
+
query_params, param_name) if query_params is not None else None
|
|
246
|
+
|
|
247
|
+
value = _populate_from_globals(param_name, value, 'queryParam', gbls)
|
|
248
|
+
|
|
249
|
+
f_name = metadata.get("field_name")
|
|
250
|
+
serialization = metadata.get('serialization', '')
|
|
251
|
+
if serialization != '':
|
|
252
|
+
serialized_parms = _get_serialized_params(
|
|
253
|
+
metadata, field.type, f_name, value)
|
|
254
|
+
for key, value in serialized_parms.items():
|
|
255
|
+
if key in params:
|
|
256
|
+
params[key].extend(value)
|
|
257
|
+
else:
|
|
258
|
+
params[key] = [value]
|
|
259
|
+
else:
|
|
260
|
+
style = metadata.get('style', 'form')
|
|
261
|
+
if style == 'deepObject':
|
|
262
|
+
params = {**params, **_get_deep_object_query_params(
|
|
263
|
+
metadata, f_name, value)}
|
|
264
|
+
elif style == 'form':
|
|
265
|
+
params = {**params, **_get_delimited_query_params(
|
|
266
|
+
metadata, f_name, value, ",")}
|
|
267
|
+
elif style == 'pipeDelimited':
|
|
268
|
+
params = {**params, **_get_delimited_query_params(
|
|
269
|
+
metadata, f_name, value, "|")}
|
|
270
|
+
else:
|
|
271
|
+
raise Exception('not yet implemented')
|
|
272
|
+
return params
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def get_headers(headers_params: Any, gbls: Optional[Dict[str, Dict[str, Dict[str, Any]]]] = None) -> Dict[str, str]:
|
|
276
|
+
if headers_params is None:
|
|
277
|
+
return {}
|
|
278
|
+
|
|
279
|
+
headers: Dict[str, str] = {}
|
|
280
|
+
|
|
281
|
+
param_fields: Tuple[Field, ...] = fields(headers_params)
|
|
282
|
+
for field in param_fields:
|
|
283
|
+
metadata = field.metadata.get('header')
|
|
284
|
+
if not metadata:
|
|
285
|
+
continue
|
|
286
|
+
|
|
287
|
+
value = _populate_from_globals(field.name, getattr(headers_params, field.name), 'header', gbls)
|
|
288
|
+
value = _serialize_header(metadata.get('explode', False), value)
|
|
289
|
+
|
|
290
|
+
if value != '':
|
|
291
|
+
headers[metadata.get('field_name', field.name)] = value
|
|
292
|
+
|
|
293
|
+
return headers
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _get_serialized_params(metadata: Dict, field_type: type, field_name: str, obj: Any) -> Dict[str, str]:
|
|
297
|
+
params: Dict[str, str] = {}
|
|
298
|
+
|
|
299
|
+
serialization = metadata.get('serialization', '')
|
|
300
|
+
if serialization == 'json':
|
|
301
|
+
params[metadata.get("field_name", field_name)
|
|
302
|
+
] = marshal_json(obj, field_type)
|
|
303
|
+
|
|
304
|
+
return params
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _get_deep_object_query_params(metadata: Dict, field_name: str, obj: Any) -> Dict[str, List[str]]:
|
|
308
|
+
params: Dict[str, List[str]] = {}
|
|
309
|
+
|
|
310
|
+
if obj is None:
|
|
311
|
+
return params
|
|
312
|
+
|
|
313
|
+
if is_dataclass(obj):
|
|
314
|
+
obj_fields: Tuple[Field, ...] = fields(obj)
|
|
315
|
+
for obj_field in obj_fields:
|
|
316
|
+
obj_param_metadata = obj_field.metadata.get('query_param')
|
|
317
|
+
if not obj_param_metadata:
|
|
318
|
+
continue
|
|
319
|
+
|
|
320
|
+
obj_val = getattr(obj, obj_field.name)
|
|
321
|
+
if obj_val is None:
|
|
322
|
+
continue
|
|
323
|
+
|
|
324
|
+
if isinstance(obj_val, List):
|
|
325
|
+
for val in obj_val:
|
|
326
|
+
if val is None:
|
|
327
|
+
continue
|
|
328
|
+
|
|
329
|
+
if params.get(
|
|
330
|
+
f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]') is None:
|
|
331
|
+
params[
|
|
332
|
+
f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]'] = [
|
|
333
|
+
]
|
|
334
|
+
|
|
335
|
+
params[
|
|
336
|
+
f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]'].append(
|
|
337
|
+
_val_to_string(val))
|
|
338
|
+
else:
|
|
339
|
+
params[
|
|
340
|
+
f'{metadata.get("field_name", field_name)}[{obj_param_metadata.get("field_name", obj_field.name)}]'] = [
|
|
341
|
+
_val_to_string(obj_val)]
|
|
342
|
+
elif isinstance(obj, Dict):
|
|
343
|
+
for key, value in obj.items():
|
|
344
|
+
if value is None:
|
|
345
|
+
continue
|
|
346
|
+
|
|
347
|
+
if isinstance(value, List):
|
|
348
|
+
for val in value:
|
|
349
|
+
if val is None:
|
|
350
|
+
continue
|
|
351
|
+
|
|
352
|
+
if params.get(f'{metadata.get("field_name", field_name)}[{key}]') is None:
|
|
353
|
+
params[f'{metadata.get("field_name", field_name)}[{key}]'] = [
|
|
354
|
+
]
|
|
355
|
+
|
|
356
|
+
params[
|
|
357
|
+
f'{metadata.get("field_name", field_name)}[{key}]'].append(_val_to_string(val))
|
|
358
|
+
else:
|
|
359
|
+
params[f'{metadata.get("field_name", field_name)}[{key}]'] = [
|
|
360
|
+
_val_to_string(value)]
|
|
361
|
+
return params
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _get_query_param_field_name(obj_field: Field) -> str:
|
|
365
|
+
obj_param_metadata = obj_field.metadata.get('query_param')
|
|
366
|
+
|
|
367
|
+
if not obj_param_metadata:
|
|
368
|
+
return ""
|
|
369
|
+
|
|
370
|
+
return obj_param_metadata.get("field_name", obj_field.name)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _get_delimited_query_params(metadata: Dict, field_name: str, obj: Any, delimiter: str) -> Dict[
|
|
374
|
+
str, List[str]]:
|
|
375
|
+
return _populate_form(field_name, metadata.get("explode", True), obj, _get_query_param_field_name, delimiter)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
SERIALIZATION_METHOD_TO_CONTENT_TYPE = {
|
|
379
|
+
'json': 'application/json',
|
|
380
|
+
'form': 'application/x-www-form-urlencoded',
|
|
381
|
+
'multipart': 'multipart/form-data',
|
|
382
|
+
'raw': 'application/octet-stream',
|
|
383
|
+
'string': 'text/plain',
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def serialize_request_body(request: Any, request_type: type, request_field_name: str, nullable: bool, optional: bool, serialization_method: str, encoder=None) -> Tuple[
|
|
388
|
+
Optional[str], Optional[Any], Optional[Any]]:
|
|
389
|
+
if request is None:
|
|
390
|
+
if not nullable and optional:
|
|
391
|
+
return None, None, None
|
|
392
|
+
|
|
393
|
+
if not is_dataclass(request) or not hasattr(request, request_field_name):
|
|
394
|
+
return serialize_content_type(request_field_name, request_type, SERIALIZATION_METHOD_TO_CONTENT_TYPE[serialization_method],
|
|
395
|
+
request, encoder)
|
|
396
|
+
|
|
397
|
+
request_val = getattr(request, request_field_name)
|
|
398
|
+
|
|
399
|
+
if request_val is None:
|
|
400
|
+
if not nullable and optional:
|
|
401
|
+
return None, None, None
|
|
402
|
+
|
|
403
|
+
request_fields: Tuple[Field, ...] = fields(request)
|
|
404
|
+
request_metadata = None
|
|
405
|
+
|
|
406
|
+
for field in request_fields:
|
|
407
|
+
if field.name == request_field_name:
|
|
408
|
+
request_metadata = field.metadata.get('request')
|
|
409
|
+
break
|
|
410
|
+
|
|
411
|
+
if request_metadata is None:
|
|
412
|
+
raise Exception('invalid request type')
|
|
413
|
+
|
|
414
|
+
return serialize_content_type(request_field_name, request_type, request_metadata.get('media_type', 'application/octet-stream'),
|
|
415
|
+
request_val)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def serialize_content_type(field_name: str, request_type: Any, media_type: str, request: Any, encoder=None) -> Tuple[Optional[str], Optional[Any], Optional[List[List[Any]]]]:
|
|
419
|
+
if re.match(r'(application|text)\/.*?\+*json.*', media_type) is not None:
|
|
420
|
+
return media_type, marshal_json(request, request_type, encoder), None
|
|
421
|
+
if re.match(r'multipart\/.*', media_type) is not None:
|
|
422
|
+
return serialize_multipart_form(media_type, request)
|
|
423
|
+
if re.match(r'application\/x-www-form-urlencoded.*', media_type) is not None:
|
|
424
|
+
return media_type, serialize_form_data(field_name, request), None
|
|
425
|
+
if isinstance(request, (bytes, bytearray)):
|
|
426
|
+
return media_type, request, None
|
|
427
|
+
if isinstance(request, str):
|
|
428
|
+
return media_type, request, None
|
|
429
|
+
|
|
430
|
+
raise Exception(
|
|
431
|
+
f"invalid request body type {type(request)} for mediaType {media_type}")
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def serialize_multipart_form(media_type: str, request: Any) -> Tuple[str, Any, List[List[Any]]]:
|
|
435
|
+
form: List[List[Any]] = []
|
|
436
|
+
request_fields = fields(request)
|
|
437
|
+
|
|
438
|
+
for field in request_fields:
|
|
439
|
+
val = getattr(request, field.name)
|
|
440
|
+
if val is None:
|
|
441
|
+
continue
|
|
442
|
+
|
|
443
|
+
field_metadata = field.metadata.get('multipart_form')
|
|
444
|
+
if not field_metadata:
|
|
445
|
+
continue
|
|
446
|
+
|
|
447
|
+
if field_metadata.get("file") is True:
|
|
448
|
+
file_fields = fields(val)
|
|
449
|
+
|
|
450
|
+
file_name = ""
|
|
451
|
+
field_name = ""
|
|
452
|
+
content = bytes()
|
|
453
|
+
|
|
454
|
+
for file_field in file_fields:
|
|
455
|
+
file_metadata = file_field.metadata.get('multipart_form')
|
|
456
|
+
if file_metadata is None:
|
|
457
|
+
continue
|
|
458
|
+
|
|
459
|
+
if file_metadata.get("content") is True:
|
|
460
|
+
content = getattr(val, file_field.name)
|
|
461
|
+
else:
|
|
462
|
+
field_name = file_metadata.get(
|
|
463
|
+
"field_name", file_field.name)
|
|
464
|
+
file_name = getattr(val, file_field.name)
|
|
465
|
+
if field_name == "" or file_name == "" or content == bytes():
|
|
466
|
+
raise Exception('invalid multipart/form-data file')
|
|
467
|
+
|
|
468
|
+
form.append([field_name, [file_name, content]])
|
|
469
|
+
elif field_metadata.get("json") is True:
|
|
470
|
+
to_append = [field_metadata.get("field_name", field.name), [
|
|
471
|
+
None, marshal_json(val, field.type), "application/json"]]
|
|
472
|
+
form.append(to_append)
|
|
473
|
+
else:
|
|
474
|
+
field_name = field_metadata.get(
|
|
475
|
+
"field_name", field.name)
|
|
476
|
+
if isinstance(val, List):
|
|
477
|
+
for value in val:
|
|
478
|
+
if value is None:
|
|
479
|
+
continue
|
|
480
|
+
form.append(
|
|
481
|
+
[field_name + "[]", [None, _val_to_string(value)]])
|
|
482
|
+
else:
|
|
483
|
+
form.append([field_name, [None, _val_to_string(val)]])
|
|
484
|
+
return media_type, None, form
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def serialize_dict(original: Dict, explode: bool, field_name, existing: Optional[Dict[str, List[str]]]) -> Dict[
|
|
488
|
+
str, List[str]]:
|
|
489
|
+
if existing is None:
|
|
490
|
+
existing = {}
|
|
491
|
+
|
|
492
|
+
if explode is True:
|
|
493
|
+
for key, val in original.items():
|
|
494
|
+
if key not in existing:
|
|
495
|
+
existing[key] = []
|
|
496
|
+
existing[key].append(val)
|
|
497
|
+
else:
|
|
498
|
+
temp = []
|
|
499
|
+
for key, val in original.items():
|
|
500
|
+
temp.append(str(key))
|
|
501
|
+
temp.append(str(val))
|
|
502
|
+
if field_name not in existing:
|
|
503
|
+
existing[field_name] = []
|
|
504
|
+
existing[field_name].append(",".join(temp))
|
|
505
|
+
return existing
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def serialize_form_data(field_name: str, data: Any) -> Dict[str, Any]:
|
|
509
|
+
form: Dict[str, List[str]] = {}
|
|
510
|
+
|
|
511
|
+
if is_dataclass(data):
|
|
512
|
+
for field in fields(data):
|
|
513
|
+
val = getattr(data, field.name)
|
|
514
|
+
if val is None:
|
|
515
|
+
continue
|
|
516
|
+
|
|
517
|
+
metadata = field.metadata.get('form')
|
|
518
|
+
if metadata is None:
|
|
519
|
+
continue
|
|
520
|
+
|
|
521
|
+
field_name = metadata.get('field_name', field.name)
|
|
522
|
+
|
|
523
|
+
if metadata.get('json'):
|
|
524
|
+
form[field_name] = [marshal_json(val, field.type)]
|
|
525
|
+
else:
|
|
526
|
+
if metadata.get('style', 'form') == 'form':
|
|
527
|
+
form = {**form, **_populate_form(
|
|
528
|
+
field_name, metadata.get('explode', True), val, _get_form_field_name, ",")}
|
|
529
|
+
else:
|
|
530
|
+
raise Exception(
|
|
531
|
+
f'Invalid form style for field {field.name}')
|
|
532
|
+
elif isinstance(data, Dict):
|
|
533
|
+
for key, value in data.items():
|
|
534
|
+
form[key] = [_val_to_string(value)]
|
|
535
|
+
else:
|
|
536
|
+
raise Exception(f'Invalid request body type for field {field_name}')
|
|
537
|
+
|
|
538
|
+
return form
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def _get_form_field_name(obj_field: Field) -> str:
|
|
542
|
+
obj_param_metadata = obj_field.metadata.get('form')
|
|
543
|
+
|
|
544
|
+
if not obj_param_metadata:
|
|
545
|
+
return ""
|
|
546
|
+
|
|
547
|
+
return obj_param_metadata.get("field_name", obj_field.name)
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _populate_form(field_name: str, explode: boolean, obj: Any, get_field_name_func: Callable, delimiter: str) -> \
|
|
551
|
+
Dict[str, List[str]]:
|
|
552
|
+
params: Dict[str, List[str]] = {}
|
|
553
|
+
|
|
554
|
+
if obj is None:
|
|
555
|
+
return params
|
|
556
|
+
|
|
557
|
+
if is_dataclass(obj):
|
|
558
|
+
items = []
|
|
559
|
+
|
|
560
|
+
obj_fields: Tuple[Field, ...] = fields(obj)
|
|
561
|
+
for obj_field in obj_fields:
|
|
562
|
+
obj_field_name = get_field_name_func(obj_field)
|
|
563
|
+
if obj_field_name == '':
|
|
564
|
+
continue
|
|
565
|
+
|
|
566
|
+
val = getattr(obj, obj_field.name)
|
|
567
|
+
if val is None:
|
|
568
|
+
continue
|
|
569
|
+
|
|
570
|
+
if explode:
|
|
571
|
+
params[obj_field_name] = [_val_to_string(val)]
|
|
572
|
+
else:
|
|
573
|
+
items.append(
|
|
574
|
+
f'{obj_field_name}{delimiter}{_val_to_string(val)}')
|
|
575
|
+
|
|
576
|
+
if len(items) > 0:
|
|
577
|
+
params[field_name] = [delimiter.join(items)]
|
|
578
|
+
elif isinstance(obj, Dict):
|
|
579
|
+
items = []
|
|
580
|
+
for key, value in obj.items():
|
|
581
|
+
if value is None:
|
|
582
|
+
continue
|
|
583
|
+
|
|
584
|
+
if explode:
|
|
585
|
+
params[key] = [_val_to_string(value)]
|
|
586
|
+
else:
|
|
587
|
+
items.append(f'{key}{delimiter}{_val_to_string(value)}')
|
|
588
|
+
|
|
589
|
+
if len(items) > 0:
|
|
590
|
+
params[field_name] = [delimiter.join(items)]
|
|
591
|
+
elif isinstance(obj, List):
|
|
592
|
+
items = []
|
|
593
|
+
|
|
594
|
+
for value in obj:
|
|
595
|
+
if value is None:
|
|
596
|
+
continue
|
|
597
|
+
|
|
598
|
+
if explode:
|
|
599
|
+
if not field_name in params:
|
|
600
|
+
params[field_name] = []
|
|
601
|
+
params[field_name].append(_val_to_string(value))
|
|
602
|
+
else:
|
|
603
|
+
items.append(_val_to_string(value))
|
|
604
|
+
|
|
605
|
+
if len(items) > 0:
|
|
606
|
+
params[field_name] = [delimiter.join(
|
|
607
|
+
[str(item) for item in items])]
|
|
608
|
+
else:
|
|
609
|
+
params[field_name] = [_val_to_string(obj)]
|
|
610
|
+
|
|
611
|
+
return params
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def _serialize_header(explode: bool, obj: Any) -> str:
|
|
615
|
+
if obj is None:
|
|
616
|
+
return ''
|
|
617
|
+
|
|
618
|
+
if is_dataclass(obj):
|
|
619
|
+
items = []
|
|
620
|
+
obj_fields: Tuple[Field, ...] = fields(obj)
|
|
621
|
+
for obj_field in obj_fields:
|
|
622
|
+
obj_param_metadata = obj_field.metadata.get('header')
|
|
623
|
+
|
|
624
|
+
if not obj_param_metadata:
|
|
625
|
+
continue
|
|
626
|
+
|
|
627
|
+
obj_field_name = obj_param_metadata.get(
|
|
628
|
+
'field_name', obj_field.name)
|
|
629
|
+
if obj_field_name == '':
|
|
630
|
+
continue
|
|
631
|
+
|
|
632
|
+
val = getattr(obj, obj_field.name)
|
|
633
|
+
if val is None:
|
|
634
|
+
continue
|
|
635
|
+
|
|
636
|
+
if explode:
|
|
637
|
+
items.append(
|
|
638
|
+
f'{obj_field_name}={_val_to_string(val)}')
|
|
639
|
+
else:
|
|
640
|
+
items.append(obj_field_name)
|
|
641
|
+
items.append(_val_to_string(val))
|
|
642
|
+
|
|
643
|
+
if len(items) > 0:
|
|
644
|
+
return ','.join(items)
|
|
645
|
+
elif isinstance(obj, Dict):
|
|
646
|
+
items = []
|
|
647
|
+
|
|
648
|
+
for key, value in obj.items():
|
|
649
|
+
if value is None:
|
|
650
|
+
continue
|
|
651
|
+
|
|
652
|
+
if explode:
|
|
653
|
+
items.append(f'{key}={_val_to_string(value)}')
|
|
654
|
+
else:
|
|
655
|
+
items.append(key)
|
|
656
|
+
items.append(_val_to_string(value))
|
|
657
|
+
|
|
658
|
+
if len(items) > 0:
|
|
659
|
+
return ','.join([str(item) for item in items])
|
|
660
|
+
elif isinstance(obj, List):
|
|
661
|
+
items = []
|
|
662
|
+
|
|
663
|
+
for value in obj:
|
|
664
|
+
if value is None:
|
|
665
|
+
continue
|
|
666
|
+
|
|
667
|
+
items.append(_val_to_string(value))
|
|
668
|
+
|
|
669
|
+
if len(items) > 0:
|
|
670
|
+
return ','.join(items)
|
|
671
|
+
else:
|
|
672
|
+
return f'{_val_to_string(obj)}'
|
|
673
|
+
|
|
674
|
+
return ''
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def unmarshal_json(data, typ, decoder=None):
|
|
678
|
+
unmarshal = make_dataclass('Unmarshal', [('res', typ)],
|
|
679
|
+
bases=(DataClassJsonMixin,))
|
|
680
|
+
json_dict = json.loads(data)
|
|
681
|
+
try:
|
|
682
|
+
out = unmarshal.from_dict({"res": json_dict})
|
|
683
|
+
except AttributeError as attr_err:
|
|
684
|
+
raise AttributeError(
|
|
685
|
+
f'unable to unmarshal {data} as {typ} - {attr_err}') from attr_err
|
|
686
|
+
|
|
687
|
+
return out.res if decoder is None else decoder(out.res)
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
def marshal_json(val, typ, encoder=None):
|
|
691
|
+
if not is_optional_type(typ) and val is None:
|
|
692
|
+
raise ValueError(
|
|
693
|
+
f"Could not marshal None into non-optional type: {typ}")
|
|
694
|
+
|
|
695
|
+
marshal = make_dataclass('Marshal', [('res', typ)],
|
|
696
|
+
bases=(DataClassJsonMixin,))
|
|
697
|
+
marshaller = marshal(res=val)
|
|
698
|
+
json_dict = marshaller.to_dict()
|
|
699
|
+
val = json_dict["res"] if encoder is None else encoder(json_dict["res"])
|
|
700
|
+
|
|
701
|
+
return json.dumps(val, separators=(',', ':'), sort_keys=True)
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def match_content_type(content_type: str, pattern: str) -> boolean:
|
|
705
|
+
if pattern in (content_type, "*", "*/*"):
|
|
706
|
+
return True
|
|
707
|
+
|
|
708
|
+
msg = Message()
|
|
709
|
+
msg['content-type'] = content_type
|
|
710
|
+
media_type = msg.get_content_type()
|
|
711
|
+
|
|
712
|
+
if media_type == pattern:
|
|
713
|
+
return True
|
|
714
|
+
|
|
715
|
+
parts = media_type.split("/")
|
|
716
|
+
if len(parts) == 2:
|
|
717
|
+
if pattern in (f'{parts[0]}/*', f'*/{parts[1]}'):
|
|
718
|
+
return True
|
|
719
|
+
|
|
720
|
+
return False
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def match_status_codes(status_codes: List[str], status_code: int) -> bool:
|
|
724
|
+
for code in status_codes:
|
|
725
|
+
if code == str(status_code):
|
|
726
|
+
return True
|
|
727
|
+
|
|
728
|
+
if code.endswith("XX") and code.startswith(str(status_code)[:1]):
|
|
729
|
+
return True
|
|
730
|
+
return False
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def datetimeisoformat(optional: bool):
|
|
734
|
+
def isoformatoptional(val):
|
|
735
|
+
if optional and val is None:
|
|
736
|
+
return None
|
|
737
|
+
return _val_to_string(val)
|
|
738
|
+
|
|
739
|
+
return isoformatoptional
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def dateisoformat(optional: bool):
|
|
743
|
+
def isoformatoptional(val):
|
|
744
|
+
if optional and val is None:
|
|
745
|
+
return None
|
|
746
|
+
return date.isoformat(val)
|
|
747
|
+
|
|
748
|
+
return isoformatoptional
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def datefromisoformat(date_str: str):
|
|
752
|
+
return dateutil.parser.parse(date_str).date()
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def bigintencoder(optional: bool):
|
|
756
|
+
def bigintencode(val: int):
|
|
757
|
+
if optional and val is None:
|
|
758
|
+
return None
|
|
759
|
+
return str(val)
|
|
760
|
+
|
|
761
|
+
return bigintencode
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
def bigintdecoder(val):
|
|
765
|
+
if isinstance(val, float):
|
|
766
|
+
raise ValueError(f"{val} is a float")
|
|
767
|
+
return int(val)
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def decimalencoder(optional: bool, as_str: bool):
|
|
771
|
+
def decimalencode(val: Decimal):
|
|
772
|
+
if optional and val is None:
|
|
773
|
+
return None
|
|
774
|
+
|
|
775
|
+
if as_str:
|
|
776
|
+
return str(val)
|
|
777
|
+
|
|
778
|
+
return float(val)
|
|
779
|
+
|
|
780
|
+
return decimalencode
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
def decimaldecoder(val):
|
|
784
|
+
return Decimal(str(val))
|
|
785
|
+
|
|
786
|
+
|
|
787
|
+
def map_encoder(optional: bool, value_encoder: Callable):
|
|
788
|
+
def map_encode(val: Dict):
|
|
789
|
+
if optional and val is None:
|
|
790
|
+
return None
|
|
791
|
+
|
|
792
|
+
encoded = {}
|
|
793
|
+
for key, value in val.items():
|
|
794
|
+
encoded[key] = value_encoder(value)
|
|
795
|
+
|
|
796
|
+
return encoded
|
|
797
|
+
|
|
798
|
+
return map_encode
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def map_decoder(value_decoder: Callable):
|
|
802
|
+
def map_decode(val: Dict):
|
|
803
|
+
decoded = {}
|
|
804
|
+
for key, value in val.items():
|
|
805
|
+
decoded[key] = value_decoder(value)
|
|
806
|
+
|
|
807
|
+
return decoded
|
|
808
|
+
|
|
809
|
+
return map_decode
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def list_encoder(optional: bool, value_encoder: Callable):
|
|
813
|
+
def list_encode(val: List):
|
|
814
|
+
if optional and val is None:
|
|
815
|
+
return None
|
|
816
|
+
|
|
817
|
+
encoded = []
|
|
818
|
+
for value in val:
|
|
819
|
+
encoded.append(value_encoder(value))
|
|
820
|
+
|
|
821
|
+
return encoded
|
|
822
|
+
|
|
823
|
+
return list_encode
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
def list_decoder(value_decoder: Callable):
|
|
827
|
+
def list_decode(val: List):
|
|
828
|
+
decoded = []
|
|
829
|
+
for value in val:
|
|
830
|
+
decoded.append(value_decoder(value))
|
|
831
|
+
|
|
832
|
+
return decoded
|
|
833
|
+
|
|
834
|
+
return list_decode
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def union_encoder(all_encoders: Dict[str, Callable]):
|
|
838
|
+
def selective_encoder(val: Any):
|
|
839
|
+
if type(val) in all_encoders:
|
|
840
|
+
return all_encoders[type(val)](val)
|
|
841
|
+
return val
|
|
842
|
+
return selective_encoder
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
def union_decoder(all_decoders: List[Callable]):
|
|
846
|
+
def selective_decoder(val: Any):
|
|
847
|
+
decoded = val
|
|
848
|
+
for decoder in all_decoders:
|
|
849
|
+
try:
|
|
850
|
+
decoded = decoder(val)
|
|
851
|
+
break
|
|
852
|
+
except (TypeError, ValueError):
|
|
853
|
+
continue
|
|
854
|
+
return decoded
|
|
855
|
+
return selective_decoder
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
def get_field_name(name):
|
|
859
|
+
def override(_, _field_name=name):
|
|
860
|
+
return _field_name
|
|
861
|
+
|
|
862
|
+
return override
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
def _val_to_string(val) -> str:
|
|
866
|
+
if isinstance(val, bool):
|
|
867
|
+
return str(val).lower()
|
|
868
|
+
if isinstance(val, datetime):
|
|
869
|
+
return str(val.isoformat().replace('+00:00', 'Z'))
|
|
870
|
+
if isinstance(val, Enum):
|
|
871
|
+
return str(val.value)
|
|
872
|
+
|
|
873
|
+
return str(val)
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
def _populate_from_globals(param_name: str, value: Any, param_type: str, gbls: Optional[Dict[str, Dict[str, Dict[str, Any]]]]):
|
|
877
|
+
if value is None and gbls is not None:
|
|
878
|
+
if 'parameters' in gbls:
|
|
879
|
+
if param_type in gbls['parameters']:
|
|
880
|
+
if param_name in gbls['parameters'][param_type]:
|
|
881
|
+
global_value = gbls['parameters'][param_type][param_name]
|
|
882
|
+
if global_value is not None:
|
|
883
|
+
value = global_value
|
|
884
|
+
|
|
885
|
+
return value
|
|
886
|
+
|
|
887
|
+
|
|
888
|
+
def decoder_with_discriminator(field_name):
|
|
889
|
+
def decode_fx(obj):
|
|
890
|
+
kls = getattr(sys.modules['sdk.models.components'], obj[field_name])
|
|
891
|
+
return unmarshal_json(json.dumps(obj), kls)
|
|
892
|
+
return decode_fx
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
def remove_suffix(input_string, suffix):
|
|
896
|
+
if suffix and input_string.endswith(suffix):
|
|
897
|
+
return input_string[:-len(suffix)]
|
|
898
|
+
return input_string
|