vantage-python 0.3.2__tar.gz → 0.3.3__tar.gz
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.
- {vantage_python-0.3.2 → vantage_python-0.3.3}/PKG-INFO +1 -1
- {vantage_python-0.3.2 → vantage_python-0.3.3}/autogen.py +150 -16
- {vantage_python-0.3.2 → vantage_python-0.3.3}/pyproject.toml +1 -1
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/_async/client.py +24 -6
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/_sync/client.py +24 -6
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/_types.py +18 -1
- {vantage_python-0.3.2 → vantage_python-0.3.3}/.github/workflows/pypi-publish.yml +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/.github/workflows/test.yml +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/.gitignore +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/LICENSE +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/Makefile +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/README.md +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/__init__.py +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/_async/__init__.py +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/_base.py +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/_sync/__init__.py +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/src/vantage/py.typed +0 -0
- {vantage_python-0.3.2 → vantage_python-0.3.3}/tests/test_e2e.py +0 -0
|
@@ -31,6 +31,12 @@ RESPONSE_HANDLERS: list[tuple[str, str, str]] = [
|
|
|
31
31
|
),
|
|
32
32
|
]
|
|
33
33
|
|
|
34
|
+
# Endpoints that return bool based on HTTP status: 404 -> False, 2xx -> True, else raise.
|
|
35
|
+
# Each entry is (METHOD, openapi_path_template).
|
|
36
|
+
BOOLEAN_STATUS_ROUTES: list[tuple[str, str]] = [
|
|
37
|
+
("GET", "/virtual_tag_configs/async/{request_id}"),
|
|
38
|
+
]
|
|
39
|
+
|
|
34
40
|
|
|
35
41
|
@dataclass
|
|
36
42
|
class Parameter:
|
|
@@ -61,6 +67,7 @@ class Endpoint:
|
|
|
61
67
|
is_multipart: bool = False
|
|
62
68
|
response_handler: str | None = None # internal client method to call, if not the default
|
|
63
69
|
response_handler_return_type: str | None = None
|
|
70
|
+
boolean_status: bool = False # 404->False, 2xx->True, else raise VantageAPIError
|
|
64
71
|
|
|
65
72
|
|
|
66
73
|
@dataclass
|
|
@@ -196,10 +203,16 @@ def preprocess_inline_models(schemas: dict[str, Any]) -> None:
|
|
|
196
203
|
existing_names.add(model_name)
|
|
197
204
|
|
|
198
205
|
|
|
199
|
-
def openapi_type_to_python(
|
|
206
|
+
def openapi_type_to_python(
|
|
207
|
+
schema: dict[str, Any],
|
|
208
|
+
schemas: dict[str, Any],
|
|
209
|
+
name_map: dict[str, str] | None = None,
|
|
210
|
+
) -> str:
|
|
200
211
|
"""Convert OpenAPI type to Python type hint."""
|
|
201
212
|
if "$ref" in schema:
|
|
202
213
|
ref_name = schema["$ref"].split("/")[-1]
|
|
214
|
+
if name_map and ref_name in name_map:
|
|
215
|
+
return name_map[ref_name]
|
|
203
216
|
return to_pascal_case(ref_name)
|
|
204
217
|
|
|
205
218
|
schema_type = schema.get("type", "any")
|
|
@@ -216,12 +229,12 @@ def openapi_type_to_python(schema: dict[str, Any], schemas: dict[str, Any]) -> s
|
|
|
216
229
|
return "bool"
|
|
217
230
|
elif schema_type == "array":
|
|
218
231
|
items = schema.get("items", {})
|
|
219
|
-
item_type = openapi_type_to_python(items, schemas)
|
|
232
|
+
item_type = openapi_type_to_python(items, schemas, name_map)
|
|
220
233
|
return f"List[{item_type}]"
|
|
221
234
|
elif schema_type == "object":
|
|
222
235
|
additional = schema.get("additionalProperties")
|
|
223
236
|
if additional:
|
|
224
|
-
value_type = openapi_type_to_python(additional, schemas)
|
|
237
|
+
value_type = openapi_type_to_python(additional, schemas, name_map)
|
|
225
238
|
return f"Dict[str, {value_type}]"
|
|
226
239
|
# Check if inline properties match an existing named schema
|
|
227
240
|
inline_props = schema.get("properties")
|
|
@@ -230,6 +243,8 @@ def openapi_type_to_python(schema: dict[str, Any], schemas: dict[str, Any]) -> s
|
|
|
230
243
|
for schema_name, schema_def in schemas.items():
|
|
231
244
|
defined_keys = sorted(schema_def.get("properties", {}).keys())
|
|
232
245
|
if defined_keys and inline_keys == defined_keys:
|
|
246
|
+
if name_map and schema_name in name_map:
|
|
247
|
+
return name_map[schema_name]
|
|
233
248
|
return to_pascal_case(schema_name)
|
|
234
249
|
return "Dict[str, Any]"
|
|
235
250
|
else:
|
|
@@ -237,7 +252,9 @@ def openapi_type_to_python(schema: dict[str, Any], schemas: dict[str, Any]) -> s
|
|
|
237
252
|
|
|
238
253
|
|
|
239
254
|
def extract_request_body_type(
|
|
240
|
-
request_body: dict[str, Any] | None,
|
|
255
|
+
request_body: dict[str, Any] | None,
|
|
256
|
+
schemas: dict[str, Any],
|
|
257
|
+
name_map: dict[str, str] | None = None,
|
|
241
258
|
) -> tuple[str | None, bool]:
|
|
242
259
|
"""Extract request body type and whether it's multipart."""
|
|
243
260
|
if not request_body:
|
|
@@ -252,13 +269,15 @@ def extract_request_body_type(
|
|
|
252
269
|
# Check for JSON
|
|
253
270
|
if "application/json" in content:
|
|
254
271
|
schema = content["application/json"].get("schema", {})
|
|
255
|
-
return openapi_type_to_python(schema, schemas), False
|
|
272
|
+
return openapi_type_to_python(schema, schemas, name_map), False
|
|
256
273
|
|
|
257
274
|
return None, False
|
|
258
275
|
|
|
259
276
|
|
|
260
277
|
def extract_response_type(
|
|
261
|
-
responses: dict[str, Any],
|
|
278
|
+
responses: dict[str, Any],
|
|
279
|
+
schemas: dict[str, Any],
|
|
280
|
+
name_map: dict[str, str] | None = None,
|
|
262
281
|
) -> str | None:
|
|
263
282
|
"""Extract successful response type."""
|
|
264
283
|
for code in ["200", "201", "202", "203"]:
|
|
@@ -268,15 +287,59 @@ def extract_response_type(
|
|
|
268
287
|
content = response.get("content", {})
|
|
269
288
|
if "application/json" in content:
|
|
270
289
|
schema = content["application/json"].get("schema", {})
|
|
271
|
-
return openapi_type_to_python(schema, schemas)
|
|
290
|
+
return openapi_type_to_python(schema, schemas, name_map)
|
|
272
291
|
return None
|
|
273
292
|
|
|
274
293
|
|
|
294
|
+
def find_request_body_schemas(schema: dict[str, Any]) -> set[str]:
|
|
295
|
+
"""Return the set of schema names referenced as request bodies in any endpoint."""
|
|
296
|
+
result = set()
|
|
297
|
+
paths = schema.get("paths", {})
|
|
298
|
+
for path_item in paths.values():
|
|
299
|
+
for method, spec in path_item.items():
|
|
300
|
+
if method in ("parameters", "servers", "summary", "description"):
|
|
301
|
+
continue
|
|
302
|
+
request_body = spec.get("requestBody", {})
|
|
303
|
+
content = request_body.get("content", {})
|
|
304
|
+
for media_type in content.values():
|
|
305
|
+
ref_schema = media_type.get("schema", {})
|
|
306
|
+
if "$ref" in ref_schema:
|
|
307
|
+
name = ref_schema["$ref"].split("/")[-1]
|
|
308
|
+
result.add(name)
|
|
309
|
+
return result
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def build_class_name_map(schemas: dict[str, Any], request_body_schemas: set[str]) -> dict[str, str]:
|
|
313
|
+
"""Build a mapping from raw schema names to Python class names, resolving conflicts.
|
|
314
|
+
|
|
315
|
+
If two schema names map to the same PascalCase name, the one used as a
|
|
316
|
+
request body is suffixed with 'Request'.
|
|
317
|
+
"""
|
|
318
|
+
initial = {name: to_pascal_case(name) for name in schemas}
|
|
319
|
+
|
|
320
|
+
by_class_name: dict[str, list[str]] = {}
|
|
321
|
+
for raw_name, class_name in initial.items():
|
|
322
|
+
by_class_name.setdefault(class_name, []).append(raw_name)
|
|
323
|
+
|
|
324
|
+
result: dict[str, str] = {}
|
|
325
|
+
for class_name, raw_names in by_class_name.items():
|
|
326
|
+
if len(raw_names) == 1:
|
|
327
|
+
result[raw_names[0]] = class_name
|
|
328
|
+
else:
|
|
329
|
+
for raw_name in raw_names:
|
|
330
|
+
if raw_name in request_body_schemas:
|
|
331
|
+
result[raw_name] = class_name + "Request"
|
|
332
|
+
else:
|
|
333
|
+
result[raw_name] = class_name
|
|
334
|
+
return result
|
|
335
|
+
|
|
336
|
+
|
|
275
337
|
def parse_endpoints(schema: dict[str, Any]) -> list[Endpoint]:
|
|
276
338
|
"""Parse all endpoints from OpenAPI schema."""
|
|
277
339
|
endpoints = []
|
|
278
340
|
paths = schema.get("paths", {})
|
|
279
341
|
schemas = schema.get("components", {}).get("schemas", {})
|
|
342
|
+
name_map = build_class_name_map(schemas, find_request_body_schemas(schema))
|
|
280
343
|
|
|
281
344
|
for path, methods in paths.items():
|
|
282
345
|
for method, spec in methods.items():
|
|
@@ -288,7 +351,7 @@ def parse_endpoints(schema: dict[str, Any]) -> list[Endpoint]:
|
|
|
288
351
|
parameters = []
|
|
289
352
|
for param in spec.get("parameters", []):
|
|
290
353
|
param_schema = param.get("schema", {})
|
|
291
|
-
param_type = openapi_type_to_python(param_schema, schemas)
|
|
354
|
+
param_type = openapi_type_to_python(param_schema, schemas, name_map)
|
|
292
355
|
parameters.append(
|
|
293
356
|
Parameter(
|
|
294
357
|
name=param["name"],
|
|
@@ -301,9 +364,9 @@ def parse_endpoints(schema: dict[str, Any]) -> list[Endpoint]:
|
|
|
301
364
|
)
|
|
302
365
|
|
|
303
366
|
request_body = spec.get("requestBody")
|
|
304
|
-
body_type, is_multipart = extract_request_body_type(request_body, schemas)
|
|
367
|
+
body_type, is_multipart = extract_request_body_type(request_body, schemas, name_map)
|
|
305
368
|
|
|
306
|
-
response_type = extract_response_type(spec.get("responses", {}), schemas)
|
|
369
|
+
response_type = extract_response_type(spec.get("responses", {}), schemas, name_map)
|
|
307
370
|
|
|
308
371
|
description = spec.get("description")
|
|
309
372
|
|
|
@@ -319,6 +382,10 @@ def parse_endpoints(schema: dict[str, Any]) -> list[Endpoint]:
|
|
|
319
382
|
if response_handler:
|
|
320
383
|
break
|
|
321
384
|
|
|
385
|
+
boolean_status = (method.upper(), path) in {
|
|
386
|
+
(m.upper(), p) for m, p in BOOLEAN_STATUS_ROUTES
|
|
387
|
+
}
|
|
388
|
+
|
|
322
389
|
endpoints.append(
|
|
323
390
|
Endpoint(
|
|
324
391
|
path=path,
|
|
@@ -336,6 +403,7 @@ def parse_endpoints(schema: dict[str, Any]) -> list[Endpoint]:
|
|
|
336
403
|
is_multipart=is_multipart,
|
|
337
404
|
response_handler=response_handler,
|
|
338
405
|
response_handler_return_type=response_handler_return_type,
|
|
406
|
+
boolean_status=boolean_status,
|
|
339
407
|
)
|
|
340
408
|
)
|
|
341
409
|
|
|
@@ -469,6 +537,7 @@ def _append_response_mapping(lines: list[str], return_type: str, data_var: str)
|
|
|
469
537
|
def generate_pydantic_models(schema: dict[str, Any]) -> str:
|
|
470
538
|
"""Generate Pydantic models from OpenAPI schemas."""
|
|
471
539
|
schemas = schema.get("components", {}).get("schemas", {})
|
|
540
|
+
name_map = build_class_name_map(schemas, find_request_body_schemas(schema))
|
|
472
541
|
lines = [
|
|
473
542
|
'"""Auto-generated Pydantic models from OpenAPI schema."""',
|
|
474
543
|
"",
|
|
@@ -482,7 +551,7 @@ def generate_pydantic_models(schema: dict[str, Any]) -> str:
|
|
|
482
551
|
]
|
|
483
552
|
|
|
484
553
|
for name, spec in schemas.items():
|
|
485
|
-
class_name = to_pascal_case(name)
|
|
554
|
+
class_name = name_map.get(name, to_pascal_case(name))
|
|
486
555
|
description = spec.get("description", "")
|
|
487
556
|
|
|
488
557
|
lines.append(f"class {class_name}(BaseModel):")
|
|
@@ -508,7 +577,7 @@ def generate_pydantic_models(schema: dict[str, Any]) -> str:
|
|
|
508
577
|
python_name = python_name + "_"
|
|
509
578
|
needs_alias = True
|
|
510
579
|
|
|
511
|
-
prop_type = openapi_type_to_python(prop_spec, schemas)
|
|
580
|
+
prop_type = openapi_type_to_python(prop_spec, schemas, name_map)
|
|
512
581
|
|
|
513
582
|
# Handle nullable
|
|
514
583
|
if prop_spec.get("x-nullable") or prop_spec.get("nullable"):
|
|
@@ -564,6 +633,21 @@ def _collect_handler_routes(resources: dict[str, Resource]) -> dict[str, list[tu
|
|
|
564
633
|
return handler_routes
|
|
565
634
|
|
|
566
635
|
|
|
636
|
+
def _collect_boolean_status_prefixes(resources: dict[str, Resource]) -> list[tuple[str, str]]:
|
|
637
|
+
"""Collect (method, path_prefix) pairs for boolean-status endpoints.
|
|
638
|
+
|
|
639
|
+
The prefix is derived by taking everything before the first path parameter
|
|
640
|
+
so it can be matched with str.startswith() at runtime.
|
|
641
|
+
"""
|
|
642
|
+
result = []
|
|
643
|
+
for resource in resources.values():
|
|
644
|
+
for endpoint in resource.endpoints:
|
|
645
|
+
if endpoint.boolean_status:
|
|
646
|
+
prefix = endpoint.path.split("{")[0]
|
|
647
|
+
result.append((endpoint.method, prefix))
|
|
648
|
+
return result
|
|
649
|
+
|
|
650
|
+
|
|
567
651
|
def generate_sync_client(resources: dict[str, Resource]) -> str:
|
|
568
652
|
"""Generate synchronous client code."""
|
|
569
653
|
lines = [
|
|
@@ -652,6 +736,29 @@ def generate_sync_client(resources: dict[str, Resource]) -> str:
|
|
|
652
736
|
" json=body,",
|
|
653
737
|
" )",
|
|
654
738
|
"",
|
|
739
|
+
]
|
|
740
|
+
)
|
|
741
|
+
|
|
742
|
+
# Inject boolean-status path checks (before the generic error check)
|
|
743
|
+
boolean_prefixes = _collect_boolean_status_prefixes(resources)
|
|
744
|
+
for method, prefix in boolean_prefixes:
|
|
745
|
+
lines.extend([
|
|
746
|
+
f' if method.upper() == "{method}" and path.startswith("{prefix}"):',
|
|
747
|
+
" if response.status_code == 404:",
|
|
748
|
+
" return False",
|
|
749
|
+
" elif response.is_success:",
|
|
750
|
+
" return True",
|
|
751
|
+
" else:",
|
|
752
|
+
" raise VantageAPIError(",
|
|
753
|
+
" status=response.status_code,",
|
|
754
|
+
" status_text=response.reason_phrase,",
|
|
755
|
+
" body=response.text,",
|
|
756
|
+
" )",
|
|
757
|
+
"",
|
|
758
|
+
])
|
|
759
|
+
|
|
760
|
+
lines.extend(
|
|
761
|
+
[
|
|
655
762
|
" if not response.is_success:",
|
|
656
763
|
" raise VantageAPIError(",
|
|
657
764
|
" status=response.status_code,",
|
|
@@ -757,7 +864,9 @@ def generate_sync_method(endpoint: Endpoint, method_name: str) -> list[str]:
|
|
|
757
864
|
|
|
758
865
|
# Method signature
|
|
759
866
|
param_str = ", ".join(["self"] + params) if params else "self"
|
|
760
|
-
if endpoint.
|
|
867
|
+
if endpoint.boolean_status:
|
|
868
|
+
return_type = "bool"
|
|
869
|
+
elif endpoint.response_handler:
|
|
761
870
|
return_type = endpoint.response_handler_return_type or "Any"
|
|
762
871
|
else:
|
|
763
872
|
return_type = endpoint.response_type or "None"
|
|
@@ -801,7 +910,7 @@ def generate_sync_method(endpoint: Endpoint, method_name: str) -> list[str]:
|
|
|
801
910
|
lines.append(" body_data = None")
|
|
802
911
|
|
|
803
912
|
# Make request and coerce response payload into typed models where possible
|
|
804
|
-
if endpoint.response_handler:
|
|
913
|
+
if endpoint.boolean_status or endpoint.response_handler:
|
|
805
914
|
lines.append(
|
|
806
915
|
f' return self._client.request("{endpoint.method}", path, params=params, body=body_data)'
|
|
807
916
|
)
|
|
@@ -907,6 +1016,29 @@ def generate_async_client(resources: dict[str, Resource]) -> str:
|
|
|
907
1016
|
" json=body,",
|
|
908
1017
|
" )",
|
|
909
1018
|
"",
|
|
1019
|
+
]
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
# Inject boolean-status path checks (before the generic error check)
|
|
1023
|
+
boolean_prefixes = _collect_boolean_status_prefixes(resources)
|
|
1024
|
+
for method, prefix in boolean_prefixes:
|
|
1025
|
+
lines.extend([
|
|
1026
|
+
f' if method.upper() == "{method}" and path.startswith("{prefix}"):',
|
|
1027
|
+
" if response.status_code == 404:",
|
|
1028
|
+
" return False",
|
|
1029
|
+
" elif response.is_success:",
|
|
1030
|
+
" return True",
|
|
1031
|
+
" else:",
|
|
1032
|
+
" raise VantageAPIError(",
|
|
1033
|
+
" status=response.status_code,",
|
|
1034
|
+
" status_text=response.reason_phrase,",
|
|
1035
|
+
" body=response.text,",
|
|
1036
|
+
" )",
|
|
1037
|
+
"",
|
|
1038
|
+
])
|
|
1039
|
+
|
|
1040
|
+
lines.extend(
|
|
1041
|
+
[
|
|
910
1042
|
" if not response.is_success:",
|
|
911
1043
|
" raise VantageAPIError(",
|
|
912
1044
|
" status=response.status_code,",
|
|
@@ -1012,7 +1144,9 @@ def generate_async_method(endpoint: Endpoint, method_name: str) -> list[str]:
|
|
|
1012
1144
|
|
|
1013
1145
|
# Method signature
|
|
1014
1146
|
param_str = ", ".join(["self"] + params) if params else "self"
|
|
1015
|
-
if endpoint.
|
|
1147
|
+
if endpoint.boolean_status:
|
|
1148
|
+
return_type = "bool"
|
|
1149
|
+
elif endpoint.response_handler:
|
|
1016
1150
|
return_type = endpoint.response_handler_return_type or "Any"
|
|
1017
1151
|
else:
|
|
1018
1152
|
return_type = endpoint.response_type or "None"
|
|
@@ -1056,7 +1190,7 @@ def generate_async_method(endpoint: Endpoint, method_name: str) -> list[str]:
|
|
|
1056
1190
|
lines.append(" body_data = None")
|
|
1057
1191
|
|
|
1058
1192
|
# Make request and coerce response payload into typed models where possible
|
|
1059
|
-
if endpoint.response_handler:
|
|
1193
|
+
if endpoint.boolean_status or endpoint.response_handler:
|
|
1060
1194
|
lines.append(
|
|
1061
1195
|
f' return await self._client.request("{endpoint.method}", path, params=params, body=body_data)'
|
|
1062
1196
|
)
|
|
@@ -116,6 +116,18 @@ class AsyncClient:
|
|
|
116
116
|
json=body,
|
|
117
117
|
)
|
|
118
118
|
|
|
119
|
+
if method.upper() == "GET" and path.startswith("/virtual_tag_configs/async/"):
|
|
120
|
+
if response.status_code == 404:
|
|
121
|
+
return False
|
|
122
|
+
elif response.is_success:
|
|
123
|
+
return True
|
|
124
|
+
else:
|
|
125
|
+
raise VantageAPIError(
|
|
126
|
+
status=response.status_code,
|
|
127
|
+
status_text=response.reason_phrase,
|
|
128
|
+
body=response.text,
|
|
129
|
+
)
|
|
130
|
+
|
|
119
131
|
if not response.is_success:
|
|
120
132
|
raise VantageAPIError(
|
|
121
133
|
status=response.status_code,
|
|
@@ -1674,7 +1686,7 @@ class InvoicesAsyncApi:
|
|
|
1674
1686
|
return Invoice.model_validate(data)
|
|
1675
1687
|
return data
|
|
1676
1688
|
|
|
1677
|
-
async def download(self, invoice_token: str, body:
|
|
1689
|
+
async def download(self, invoice_token: str, body: DownloadInvoiceRequest) -> DownloadInvoice:
|
|
1678
1690
|
"""
|
|
1679
1691
|
Get invoice file
|
|
1680
1692
|
|
|
@@ -1683,7 +1695,10 @@ class InvoicesAsyncApi:
|
|
|
1683
1695
|
path = f"/invoices/{quote(str(invoice_token), safe='')}/download"
|
|
1684
1696
|
params = None
|
|
1685
1697
|
body_data = body.model_dump(by_alias=True, exclude_none=True) if hasattr(body, 'model_dump') else body
|
|
1686
|
-
await self._client.request("POST", path, params=params, body=body_data)
|
|
1698
|
+
data = await self._client.request("POST", path, params=params, body=body_data)
|
|
1699
|
+
if isinstance(data, dict):
|
|
1700
|
+
return DownloadInvoice.model_validate(data)
|
|
1701
|
+
return data
|
|
1687
1702
|
|
|
1688
1703
|
async def send(self, invoice_token: str) -> SendInvoice:
|
|
1689
1704
|
"""
|
|
@@ -3042,7 +3057,7 @@ class VirtualTagConfigsAsyncApi:
|
|
|
3042
3057
|
return VirtualTagConfigStatus.model_validate(data)
|
|
3043
3058
|
return data
|
|
3044
3059
|
|
|
3045
|
-
async def update_async(self, token: str, body: UpdateAsyncVirtualTagConfig) ->
|
|
3060
|
+
async def update_async(self, token: str, body: UpdateAsyncVirtualTagConfig) -> AsyncVirtualTagConfigUpdate:
|
|
3046
3061
|
"""
|
|
3047
3062
|
Update virtual tag config asynchronously
|
|
3048
3063
|
|
|
@@ -3051,9 +3066,12 @@ class VirtualTagConfigsAsyncApi:
|
|
|
3051
3066
|
path = f"/virtual_tag_configs/{quote(str(token), safe='')}/async"
|
|
3052
3067
|
params = None
|
|
3053
3068
|
body_data = body.model_dump(by_alias=True, exclude_none=True) if hasattr(body, 'model_dump') else body
|
|
3054
|
-
await self._client.request("PUT", path, params=params, body=body_data)
|
|
3069
|
+
data = await self._client.request("PUT", path, params=params, body=body_data)
|
|
3070
|
+
if isinstance(data, dict):
|
|
3071
|
+
return AsyncVirtualTagConfigUpdate.model_validate(data)
|
|
3072
|
+
return data
|
|
3055
3073
|
|
|
3056
|
-
async def get_async_virtual_tag_config_status(self, request_id: str) ->
|
|
3074
|
+
async def get_async_virtual_tag_config_status(self, request_id: str) -> bool:
|
|
3057
3075
|
"""
|
|
3058
3076
|
Get async virtual tag config update status
|
|
3059
3077
|
|
|
@@ -3062,7 +3080,7 @@ class VirtualTagConfigsAsyncApi:
|
|
|
3062
3080
|
path = f"/virtual_tag_configs/async/{quote(str(request_id), safe='')}"
|
|
3063
3081
|
params = None
|
|
3064
3082
|
body_data = None
|
|
3065
|
-
await self._client.request("GET", path, params=params, body=body_data)
|
|
3083
|
+
return await self._client.request("GET", path, params=params, body=body_data)
|
|
3066
3084
|
|
|
3067
3085
|
|
|
3068
3086
|
class WorkspacesAsyncApi:
|
|
@@ -116,6 +116,18 @@ class SyncClient:
|
|
|
116
116
|
json=body,
|
|
117
117
|
)
|
|
118
118
|
|
|
119
|
+
if method.upper() == "GET" and path.startswith("/virtual_tag_configs/async/"):
|
|
120
|
+
if response.status_code == 404:
|
|
121
|
+
return False
|
|
122
|
+
elif response.is_success:
|
|
123
|
+
return True
|
|
124
|
+
else:
|
|
125
|
+
raise VantageAPIError(
|
|
126
|
+
status=response.status_code,
|
|
127
|
+
status_text=response.reason_phrase,
|
|
128
|
+
body=response.text,
|
|
129
|
+
)
|
|
130
|
+
|
|
119
131
|
if not response.is_success:
|
|
120
132
|
raise VantageAPIError(
|
|
121
133
|
status=response.status_code,
|
|
@@ -1674,7 +1686,7 @@ class InvoicesApi:
|
|
|
1674
1686
|
return Invoice.model_validate(data)
|
|
1675
1687
|
return data
|
|
1676
1688
|
|
|
1677
|
-
def download(self, invoice_token: str, body:
|
|
1689
|
+
def download(self, invoice_token: str, body: DownloadInvoiceRequest) -> DownloadInvoice:
|
|
1678
1690
|
"""
|
|
1679
1691
|
Get invoice file
|
|
1680
1692
|
|
|
@@ -1683,7 +1695,10 @@ class InvoicesApi:
|
|
|
1683
1695
|
path = f"/invoices/{quote(str(invoice_token), safe='')}/download"
|
|
1684
1696
|
params = None
|
|
1685
1697
|
body_data = body.model_dump(by_alias=True, exclude_none=True) if hasattr(body, 'model_dump') else body
|
|
1686
|
-
self._client.request("POST", path, params=params, body=body_data)
|
|
1698
|
+
data = self._client.request("POST", path, params=params, body=body_data)
|
|
1699
|
+
if isinstance(data, dict):
|
|
1700
|
+
return DownloadInvoice.model_validate(data)
|
|
1701
|
+
return data
|
|
1687
1702
|
|
|
1688
1703
|
def send(self, invoice_token: str) -> SendInvoice:
|
|
1689
1704
|
"""
|
|
@@ -3042,7 +3057,7 @@ class VirtualTagConfigsApi:
|
|
|
3042
3057
|
return VirtualTagConfigStatus.model_validate(data)
|
|
3043
3058
|
return data
|
|
3044
3059
|
|
|
3045
|
-
def update_async(self, token: str, body: UpdateAsyncVirtualTagConfig) ->
|
|
3060
|
+
def update_async(self, token: str, body: UpdateAsyncVirtualTagConfig) -> AsyncVirtualTagConfigUpdate:
|
|
3046
3061
|
"""
|
|
3047
3062
|
Update virtual tag config asynchronously
|
|
3048
3063
|
|
|
@@ -3051,9 +3066,12 @@ class VirtualTagConfigsApi:
|
|
|
3051
3066
|
path = f"/virtual_tag_configs/{quote(str(token), safe='')}/async"
|
|
3052
3067
|
params = None
|
|
3053
3068
|
body_data = body.model_dump(by_alias=True, exclude_none=True) if hasattr(body, 'model_dump') else body
|
|
3054
|
-
self._client.request("PUT", path, params=params, body=body_data)
|
|
3069
|
+
data = self._client.request("PUT", path, params=params, body=body_data)
|
|
3070
|
+
if isinstance(data, dict):
|
|
3071
|
+
return AsyncVirtualTagConfigUpdate.model_validate(data)
|
|
3072
|
+
return data
|
|
3055
3073
|
|
|
3056
|
-
def get_async_virtual_tag_config_status(self, request_id: str) ->
|
|
3074
|
+
def get_async_virtual_tag_config_status(self, request_id: str) -> bool:
|
|
3057
3075
|
"""
|
|
3058
3076
|
Get async virtual tag config update status
|
|
3059
3077
|
|
|
@@ -3062,7 +3080,7 @@ class VirtualTagConfigsApi:
|
|
|
3062
3080
|
path = f"/virtual_tag_configs/async/{quote(str(request_id), safe='')}"
|
|
3063
3081
|
params = None
|
|
3064
3082
|
body_data = None
|
|
3065
|
-
self._client.request("GET", path, params=params, body=body_data)
|
|
3083
|
+
return self._client.request("GET", path, params=params, body=body_data)
|
|
3066
3084
|
|
|
3067
3085
|
|
|
3068
3086
|
class WorkspacesApi:
|
|
@@ -1021,12 +1021,18 @@ class CreateInvoice(BaseModel):
|
|
|
1021
1021
|
account_token: str = Field(description="Token of the managed account to invoice")
|
|
1022
1022
|
|
|
1023
1023
|
|
|
1024
|
-
class
|
|
1024
|
+
class DownloadInvoiceRequest(BaseModel):
|
|
1025
1025
|
"""Download invoice file (PDF or CSV)."""
|
|
1026
1026
|
|
|
1027
1027
|
file_type: str = Field(description="Type of file to download (pdf or csv)")
|
|
1028
1028
|
|
|
1029
1029
|
|
|
1030
|
+
class DownloadInvoice(BaseModel):
|
|
1031
|
+
"""DownloadInvoice model"""
|
|
1032
|
+
|
|
1033
|
+
download_url: str = Field(description="The URL to download the invoice file.")
|
|
1034
|
+
|
|
1035
|
+
|
|
1030
1036
|
class SendInvoice(BaseModel):
|
|
1031
1037
|
"""SendInvoice model"""
|
|
1032
1038
|
|
|
@@ -1120,6 +1126,8 @@ class ManagedAccount(BaseModel):
|
|
|
1120
1126
|
billing_rule_tokens: List[str] = Field(description="The tokens for the Billing Rules assigned to the Managed Account.")
|
|
1121
1127
|
email_domain: Optional[str] = Field(default=None, description="Email domain associated with this Managed Account for SSO.")
|
|
1122
1128
|
msp_billing_profile_token: Optional[str] = Field(default=None, description="Token of the MSP billing profile used for this managed account (MSP invoicing accounts only)")
|
|
1129
|
+
payment_terms_days: Optional[int] = Field(default=None, description="Number of days until payment is due after invoice date (MSP invoicing accounts only)")
|
|
1130
|
+
include_managed_account_integrations: Optional[bool] = Field(default=None, description="Whether to include managed account's own integrations in invoice cost calculations (MSP invoicing accounts only)")
|
|
1123
1131
|
billing_information_attributes: Optional[BillingInformation] = Field(default=None)
|
|
1124
1132
|
business_information_attributes: Optional[BusinessInformation] = Field(default=None)
|
|
1125
1133
|
|
|
@@ -1143,6 +1151,8 @@ class UpdateManagedAccount(BaseModel):
|
|
|
1143
1151
|
billing_rule_tokens: Optional[List[str]] = Field(default=None, description="Billing Rule tokens to assign to the Managed Account.")
|
|
1144
1152
|
email_domain: Optional[str] = Field(default=None, description="Email domain to associate with this Managed Account for SSO.")
|
|
1145
1153
|
msp_billing_profile_token: Optional[str] = Field(default=None, description="Token of the MSP billing profile to use for this managed account (MSP invoicing accounts only).")
|
|
1154
|
+
payment_terms_days: Optional[int] = Field(default=None, description="Number of days until payment is due after invoice date (MSP invoicing accounts only). Defaults to 10.")
|
|
1155
|
+
include_managed_account_integrations: Optional[bool] = Field(default=None, description="Whether to include managed account's own integrations in invoice cost calculations (MSP invoicing accounts only). Defaults to false.")
|
|
1146
1156
|
billing_information_attributes: Optional[BillingInformationAttributes] = Field(default=None, description="Billing address and contact information (MSP invoicing accounts only)")
|
|
1147
1157
|
business_information_attributes: Optional[BusinessInformationAttributes] = Field(default=None, description="Business information and custom fields (MSP invoicing accounts only)")
|
|
1148
1158
|
|
|
@@ -1847,6 +1857,13 @@ class UpdateAsyncVirtualTagConfig(BaseModel):
|
|
|
1847
1857
|
values: Optional[List[VirtualTagConfigValue]] = Field(default=None, description="Values for the VirtualTagConfig, with match precedence determined by order in the list.")
|
|
1848
1858
|
|
|
1849
1859
|
|
|
1860
|
+
class AsyncVirtualTagConfigUpdate(BaseModel):
|
|
1861
|
+
"""AsyncVirtualTagConfigUpdate model"""
|
|
1862
|
+
|
|
1863
|
+
request_id: str = Field(description="The request ID of the async virtual tag config update.")
|
|
1864
|
+
status_url: str = Field(description="The status path of the async virtual tag config update.")
|
|
1865
|
+
|
|
1866
|
+
|
|
1850
1867
|
class Workspaces(BaseModel):
|
|
1851
1868
|
"""Workspaces model"""
|
|
1852
1869
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|