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.
Files changed (154) hide show
  1. plex_api/__init__.py +4 -0
  2. plex_api/_hooks/__init__.py +5 -0
  3. plex_api/_hooks/registration.py +13 -0
  4. plex_api/_hooks/sdkhooks.py +57 -0
  5. plex_api/_hooks/types.py +74 -0
  6. plex_api/activities.py +151 -0
  7. plex_api/authentication.py +146 -0
  8. plex_api/butler.py +333 -0
  9. plex_api/hubs.py +158 -0
  10. plex_api/library.py +808 -0
  11. plex_api/log.py +225 -0
  12. plex_api/media.py +210 -0
  13. plex_api/models/__init__.py +4 -0
  14. plex_api/models/components/__init__.py +5 -0
  15. plex_api/models/components/security.py +11 -0
  16. plex_api/models/errors/__init__.py +63 -0
  17. plex_api/models/errors/addplaylistcontents.py +32 -0
  18. plex_api/models/errors/applyupdates.py +32 -0
  19. plex_api/models/errors/cancelserveractivities.py +32 -0
  20. plex_api/models/errors/checkforupdates.py +32 -0
  21. plex_api/models/errors/clearplaylistcontents.py +32 -0
  22. plex_api/models/errors/createplaylist.py +32 -0
  23. plex_api/models/errors/deletelibrary.py +32 -0
  24. plex_api/models/errors/deleteplaylist.py +32 -0
  25. plex_api/models/errors/enablepapertrail.py +32 -0
  26. plex_api/models/errors/getavailableclients.py +32 -0
  27. plex_api/models/errors/getbutlertasks.py +32 -0
  28. plex_api/models/errors/getdevices.py +32 -0
  29. plex_api/models/errors/getfilehash.py +32 -0
  30. plex_api/models/errors/getglobalhubs.py +32 -0
  31. plex_api/models/errors/getlibraries.py +32 -0
  32. plex_api/models/errors/getlibrary.py +32 -0
  33. plex_api/models/errors/getlibraryhubs.py +32 -0
  34. plex_api/models/errors/getmetadata.py +32 -0
  35. plex_api/models/errors/getmetadatachildren.py +32 -0
  36. plex_api/models/errors/getmyplexaccount.py +32 -0
  37. plex_api/models/errors/getondeck.py +32 -0
  38. plex_api/models/errors/getpin.py +32 -0
  39. plex_api/models/errors/getplaylist.py +32 -0
  40. plex_api/models/errors/getplaylistcontents.py +32 -0
  41. plex_api/models/errors/getplaylists.py +32 -0
  42. plex_api/models/errors/getrecentlyadded.py +32 -0
  43. plex_api/models/errors/getresizedphoto.py +32 -0
  44. plex_api/models/errors/getsearchresults.py +32 -0
  45. plex_api/models/errors/getserveractivities.py +32 -0
  46. plex_api/models/errors/getservercapabilities.py +32 -0
  47. plex_api/models/errors/getserveridentity.py +32 -0
  48. plex_api/models/errors/getserverlist.py +32 -0
  49. plex_api/models/errors/getserverpreferences.py +32 -0
  50. plex_api/models/errors/getsessionhistory.py +32 -0
  51. plex_api/models/errors/getsessions.py +32 -0
  52. plex_api/models/errors/getsourceconnectioninformation.py +32 -0
  53. plex_api/models/errors/getstatistics.py +32 -0
  54. plex_api/models/errors/gettimeline.py +32 -0
  55. plex_api/models/errors/gettoken.py +32 -0
  56. plex_api/models/errors/gettranscodesessions.py +32 -0
  57. plex_api/models/errors/gettransienttoken.py +32 -0
  58. plex_api/models/errors/getupdatestatus.py +32 -0
  59. plex_api/models/errors/logline.py +32 -0
  60. plex_api/models/errors/logmultiline.py +32 -0
  61. plex_api/models/errors/markplayed.py +32 -0
  62. plex_api/models/errors/markunplayed.py +32 -0
  63. plex_api/models/errors/performsearch.py +32 -0
  64. plex_api/models/errors/performvoicesearch.py +32 -0
  65. plex_api/models/errors/refreshlibrary.py +32 -0
  66. plex_api/models/errors/sdkerror.py +24 -0
  67. plex_api/models/errors/startalltasks.py +32 -0
  68. plex_api/models/errors/starttask.py +32 -0
  69. plex_api/models/errors/startuniversaltranscode.py +32 -0
  70. plex_api/models/errors/stopalltasks.py +32 -0
  71. plex_api/models/errors/stoptask.py +32 -0
  72. plex_api/models/errors/stoptranscodesession.py +32 -0
  73. plex_api/models/errors/updateplaylist.py +32 -0
  74. plex_api/models/errors/updateplayprogress.py +32 -0
  75. plex_api/models/errors/uploadplaylist.py +32 -0
  76. plex_api/models/operations/__init__.py +64 -0
  77. plex_api/models/operations/addplaylistcontents.py +74 -0
  78. plex_api/models/operations/applyupdates.py +39 -0
  79. plex_api/models/operations/cancelserveractivities.py +25 -0
  80. plex_api/models/operations/checkforupdates.py +32 -0
  81. plex_api/models/operations/clearplaylistcontents.py +25 -0
  82. plex_api/models/operations/createplaylist.py +91 -0
  83. plex_api/models/operations/deletelibrary.py +25 -0
  84. plex_api/models/operations/deleteplaylist.py +25 -0
  85. plex_api/models/operations/enablepapertrail.py +17 -0
  86. plex_api/models/operations/getavailableclients.py +58 -0
  87. plex_api/models/operations/getbutlertasks.py +52 -0
  88. plex_api/models/operations/getdevices.py +53 -0
  89. plex_api/models/operations/getfilehash.py +28 -0
  90. plex_api/models/operations/getglobalhubs.py +100 -0
  91. plex_api/models/operations/getlibraries.py +79 -0
  92. plex_api/models/operations/getlibrary.py +153 -0
  93. plex_api/models/operations/getlibraryhubs.py +205 -0
  94. plex_api/models/operations/getlibraryitems.py +227 -0
  95. plex_api/models/operations/getmetadata.py +264 -0
  96. plex_api/models/operations/getmetadatachildren.py +115 -0
  97. plex_api/models/operations/getmyplexaccount.py +50 -0
  98. plex_api/models/operations/getondeck.py +166 -0
  99. plex_api/models/operations/getpin.py +86 -0
  100. plex_api/models/operations/getplaylist.py +70 -0
  101. plex_api/models/operations/getplaylistcontents.py +178 -0
  102. plex_api/models/operations/getplaylists.py +85 -0
  103. plex_api/models/operations/getrecentlyadded.py +166 -0
  104. plex_api/models/operations/getresizedphoto.py +48 -0
  105. plex_api/models/operations/getsearchresults.py +182 -0
  106. plex_api/models/operations/getserveractivities.py +63 -0
  107. plex_api/models/operations/getservercapabilities.py +99 -0
  108. plex_api/models/operations/getserveridentity.py +42 -0
  109. plex_api/models/operations/getserverlist.py +53 -0
  110. plex_api/models/operations/getserverpreferences.py +57 -0
  111. plex_api/models/operations/getsessionhistory.py +67 -0
  112. plex_api/models/operations/getsessions.py +185 -0
  113. plex_api/models/operations/getsourceconnectioninformation.py +25 -0
  114. plex_api/models/operations/getstatistics.py +93 -0
  115. plex_api/models/operations/gettimeline.py +50 -0
  116. plex_api/models/operations/gettoken.py +35 -0
  117. plex_api/models/operations/gettranscodesessions.py +71 -0
  118. plex_api/models/operations/gettransienttoken.py +36 -0
  119. plex_api/models/operations/getupdatestatus.py +57 -0
  120. plex_api/models/operations/logline.py +50 -0
  121. plex_api/models/operations/logmultiline.py +17 -0
  122. plex_api/models/operations/markplayed.py +25 -0
  123. plex_api/models/operations/markunplayed.py +25 -0
  124. plex_api/models/operations/performsearch.py +30 -0
  125. plex_api/models/operations/performvoicesearch.py +30 -0
  126. plex_api/models/operations/refreshlibrary.py +25 -0
  127. plex_api/models/operations/searchlibrary.py +96 -0
  128. plex_api/models/operations/startalltasks.py +17 -0
  129. plex_api/models/operations/starttask.py +43 -0
  130. plex_api/models/operations/startuniversaltranscode.py +56 -0
  131. plex_api/models/operations/stopalltasks.py +17 -0
  132. plex_api/models/operations/stoptask.py +43 -0
  133. plex_api/models/operations/stoptranscodesession.py +25 -0
  134. plex_api/models/operations/updateplaylist.py +30 -0
  135. plex_api/models/operations/updateplayprogress.py +29 -0
  136. plex_api/models/operations/uploadplaylist.py +47 -0
  137. plex_api/playlists.py +629 -0
  138. plex_api/plex.py +154 -0
  139. plex_api/sdk.py +180 -0
  140. plex_api/sdkconfiguration.py +52 -0
  141. plex_api/search.py +232 -0
  142. plex_api/server.py +525 -0
  143. plex_api/sessions.py +272 -0
  144. plex_api/statistics.py +86 -0
  145. plex_api/updater.py +212 -0
  146. plex_api/utils/__init__.py +4 -0
  147. plex_api/utils/retries.py +120 -0
  148. plex_api/utils/utils.py +898 -0
  149. plex_api/video.py +136 -0
  150. plex_api_client-0.6.1.dist-info/LICENSE.md +21 -0
  151. plex_api_client-0.6.1.dist-info/METADATA +397 -0
  152. plex_api_client-0.6.1.dist-info/RECORD +154 -0
  153. plex_api_client-0.6.1.dist-info/WHEEL +5 -0
  154. plex_api_client-0.6.1.dist-info/top_level.txt +1 -0
@@ -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