thrustlab 0.1.0__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 (119) hide show
  1. thrustlab/__init__.py +23 -0
  2. thrustlab/_async_polling.py +47 -0
  3. thrustlab/_http.py +166 -0
  4. thrustlab/_models/__init__.py +211 -0
  5. thrustlab/_models/api_key.py +184 -0
  6. thrustlab/_models/api_key_create.py +74 -0
  7. thrustlab/_models/api_key_with_secret.py +192 -0
  8. thrustlab/_models/billing_portal_session_resource.py +74 -0
  9. thrustlab/_models/checkout_session_resource.py +94 -0
  10. thrustlab/_models/component_brand.py +80 -0
  11. thrustlab/_models/component_brands_response.py +108 -0
  12. thrustlab/_models/component_create_body.py +83 -0
  13. thrustlab/_models/component_create_body_spec_json.py +46 -0
  14. thrustlab/_models/component_list_response.py +108 -0
  15. thrustlab/_models/component_patch.py +111 -0
  16. thrustlab/_models/component_patch_spec_json_type_0.py +46 -0
  17. thrustlab/_models/component_resource.py +188 -0
  18. thrustlab/_models/component_resource_spec_json.py +46 -0
  19. thrustlab/_models/create_webhook_endpoint_v1_webhook_endpoints_post_response_create_webhook_endpoint_v1_webhook_endpoints_post.py +50 -0
  20. thrustlab/_models/credit_balance_resource.py +135 -0
  21. thrustlab/_models/credit_bucket_breakdown.py +92 -0
  22. thrustlab/_models/credit_bucket_breakdown_type.py +9 -0
  23. thrustlab/_models/credit_usage_event_resource.py +154 -0
  24. thrustlab/_models/credit_usage_event_resource_credit_type.py +9 -0
  25. thrustlab/_models/credit_usage_event_resource_type.py +16 -0
  26. thrustlab/_models/credit_usage_list_response.py +116 -0
  27. thrustlab/_models/credit_usage_related_resource.py +71 -0
  28. thrustlab/_models/credit_usage_related_resource_object.py +9 -0
  29. thrustlab/_models/fire_test_event_v1_webhook_endpoints_public_id_test_post_response_fire_test_event_v1_webhook_endpoints_public_id_test_post.py +48 -0
  30. thrustlab/_models/get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get_response_get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get.py +49 -0
  31. thrustlab/_models/get_event_v1_events_event_id_get_response_get_event_v1_events_event_id_get.py +46 -0
  32. thrustlab/_models/get_webhook_endpoint_v1_webhook_endpoints_public_id_get_response_get_webhook_endpoint_v1_webhook_endpoints_public_id_get.py +48 -0
  33. thrustlab/_models/http_validation_error.py +79 -0
  34. thrustlab/_models/list_deliveries_v1_webhook_endpoints_public_id_deliveries_get_response_list_deliveries_v1_webhook_endpoints_public_id_deliveries_get.py +49 -0
  35. thrustlab/_models/list_events_v1_events_get_response_list_events_v1_events_get.py +46 -0
  36. thrustlab/_models/list_response_api_key.py +116 -0
  37. thrustlab/_models/list_webhook_endpoints_v1_webhook_endpoints_get_response_list_webhook_endpoints_v1_webhook_endpoints_get.py +46 -0
  38. thrustlab/_models/patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch_response_patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch.py +49 -0
  39. thrustlab/_models/portal_session_in.py +42 -0
  40. thrustlab/_models/project_create_body.py +103 -0
  41. thrustlab/_models/project_list_response.py +108 -0
  42. thrustlab/_models/project_patch.py +113 -0
  43. thrustlab/_models/project_resource.py +127 -0
  44. thrustlab/_models/retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post_response_retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post.py +49 -0
  45. thrustlab/_models/rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post_response_rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post.py +49 -0
  46. thrustlab/_models/rotor_group_in.py +107 -0
  47. thrustlab/_models/rotor_group_resource.py +97 -0
  48. thrustlab/_models/simulation_cancel_body.py +24 -0
  49. thrustlab/_models/simulation_create_body.py +128 -0
  50. thrustlab/_models/simulation_inputs.py +66 -0
  51. thrustlab/_models/simulation_list_response.py +88 -0
  52. thrustlab/_models/simulation_patch.py +121 -0
  53. thrustlab/_models/simulation_patch_plot_config_json_type_0_item.py +46 -0
  54. thrustlab/_models/simulation_resource.py +283 -0
  55. thrustlab/_models/simulation_resource_error_type_0.py +46 -0
  56. thrustlab/_models/simulation_resource_plot_config_json_type_0_item.py +46 -0
  57. thrustlab/_models/simulation_resource_result_type_0.py +46 -0
  58. thrustlab/_models/starred_component_create_body.py +80 -0
  59. thrustlab/_models/starred_component_list_response.py +108 -0
  60. thrustlab/_models/starred_component_resource.py +106 -0
  61. thrustlab/_models/submission_create_body.py +121 -0
  62. thrustlab/_models/submission_create_body_data_json.py +46 -0
  63. thrustlab/_models/submission_list_response.py +108 -0
  64. thrustlab/_models/submission_patch.py +174 -0
  65. thrustlab/_models/submission_patch_data_json_type_0.py +46 -0
  66. thrustlab/_models/submission_resource.py +197 -0
  67. thrustlab/_models/submission_resource_data_json.py +46 -0
  68. thrustlab/_models/subscription_create.py +58 -0
  69. thrustlab/_models/subscription_resource.py +168 -0
  70. thrustlab/_models/subscription_resource_status.py +12 -0
  71. thrustlab/_models/subscription_resource_tier.py +12 -0
  72. thrustlab/_models/sweep_cancel_body.py +42 -0
  73. thrustlab/_models/sweep_config_in.py +172 -0
  74. thrustlab/_models/sweep_create_body.py +138 -0
  75. thrustlab/_models/sweep_inputs.py +66 -0
  76. thrustlab/_models/sweep_list_response.py +88 -0
  77. thrustlab/_models/sweep_param_range_in.py +136 -0
  78. thrustlab/_models/sweep_param_range_in_mode.py +9 -0
  79. thrustlab/_models/sweep_patch.py +121 -0
  80. thrustlab/_models/sweep_patch_plot_config_json_type_0_item.py +46 -0
  81. thrustlab/_models/sweep_point_list_response.py +88 -0
  82. thrustlab/_models/sweep_point_resource.py +103 -0
  83. thrustlab/_models/sweep_point_resource_inputs.py +46 -0
  84. thrustlab/_models/sweep_point_resource_rotors_type_0.py +46 -0
  85. thrustlab/_models/sweep_resource.py +327 -0
  86. thrustlab/_models/sweep_resource_error_type_0.py +46 -0
  87. thrustlab/_models/sweep_resource_plot_config_json_type_0_item.py +46 -0
  88. thrustlab/_models/sweep_resource_summary_type_0.py +46 -0
  89. thrustlab/_models/sweep_resource_sweep_config_type_0.py +46 -0
  90. thrustlab/_models/sweep_rotor_group_in.py +95 -0
  91. thrustlab/_models/types.py +55 -0
  92. thrustlab/_models/user_patch.py +73 -0
  93. thrustlab/_models/user_resource.py +170 -0
  94. thrustlab/_models/v1_health_v1_health_get_response_v1_health_v1_health_get.py +46 -0
  95. thrustlab/_models/validation_error.py +90 -0
  96. thrustlab/_models/webhook_endpoint_create.py +91 -0
  97. thrustlab/_models/webhook_endpoint_patch.py +144 -0
  98. thrustlab/_pagination.py +109 -0
  99. thrustlab/_version.py +3 -0
  100. thrustlab/client.py +100 -0
  101. thrustlab/exceptions.py +121 -0
  102. thrustlab/resources/__init__.py +1 -0
  103. thrustlab/resources/_base.py +12 -0
  104. thrustlab/resources/api_keys.py +43 -0
  105. thrustlab/resources/components.py +60 -0
  106. thrustlab/resources/credits.py +40 -0
  107. thrustlab/resources/projects.py +54 -0
  108. thrustlab/resources/simulations.py +74 -0
  109. thrustlab/resources/starred_components.py +57 -0
  110. thrustlab/resources/submissions.py +44 -0
  111. thrustlab/resources/subscriptions.py +42 -0
  112. thrustlab/resources/sweeps.py +96 -0
  113. thrustlab/resources/users.py +13 -0
  114. thrustlab/resources/webhook_endpoints.py +138 -0
  115. thrustlab/webhooks.py +121 -0
  116. thrustlab-0.1.0.dist-info/METADATA +190 -0
  117. thrustlab-0.1.0.dist-info/RECORD +119 -0
  118. thrustlab-0.1.0.dist-info/WHEEL +4 -0
  119. thrustlab-0.1.0.dist-info/licenses/LICENSE +21 -0
thrustlab/__init__.py ADDED
@@ -0,0 +1,23 @@
1
+ """ThrustLab — official Python SDK for the ThrustLab API.
2
+
3
+ See https://thrustlab.com/docs/sdk/python for usage.
4
+ """
5
+
6
+ from thrustlab._version import __version__
7
+ from thrustlab.client import Client
8
+ from thrustlab.exceptions import (
9
+ APIError, APIServerError, AuthenticationError, ConfigurationError,
10
+ ConflictError, ForbiddenError, NetworkError, NotFoundError, RateLimitError,
11
+ SignatureVerificationError, ThrustlabError, ValidationError,
12
+ )
13
+ from thrustlab.webhooks import Event, Webhook
14
+
15
+ __all__ = [
16
+ "__version__",
17
+ "Client",
18
+ "APIError", "APIServerError", "AuthenticationError", "ConfigurationError",
19
+ "ConflictError", "ForbiddenError", "NetworkError", "NotFoundError",
20
+ "RateLimitError", "SignatureVerificationError", "ThrustlabError",
21
+ "ValidationError",
22
+ "Event", "Webhook",
23
+ ]
@@ -0,0 +1,47 @@
1
+ """Polling helpers for async resources (simulations, sweeps).
2
+
3
+ The terminal states are: 'succeeded' | 'failed' | 'cancelled'.
4
+ 'timed_out' is an SDK-side sentinel set when wait() exceeds its timeout budget
5
+ — it is never returned by the server.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import time
11
+ from typing import Any, Callable
12
+
13
+ TERMINAL_STATES = {"succeeded", "failed", "cancelled"}
14
+
15
+
16
+ def poll_until_terminal(
17
+ fetch: Callable[[], dict[str, Any]],
18
+ *,
19
+ timeout: float = 600.0,
20
+ poll_interval: float = 2.0,
21
+ ) -> dict[str, Any]:
22
+ """Poll ``fetch`` until the returned object reaches a terminal state.
23
+
24
+ Args:
25
+ fetch: Zero-argument callable that returns the latest resource dict.
26
+ timeout: Maximum seconds to wait. If exceeded, returns the last dict
27
+ with ``status`` set to ``"timed_out"``.
28
+ poll_interval: Seconds between polls (``time.sleep``).
29
+
30
+ Returns:
31
+ The resource dict when status is in ``TERMINAL_STATES``, or a copy
32
+ with ``status="timed_out"`` if the deadline was exceeded.
33
+ """
34
+ deadline = time.monotonic() + timeout
35
+ attempt = 0
36
+ while True:
37
+ obj = fetch()
38
+ status = obj.get("status")
39
+ if status in TERMINAL_STATES:
40
+ return obj
41
+ if time.monotonic() >= deadline:
42
+ obj = dict(obj)
43
+ obj["status"] = "timed_out"
44
+ return obj
45
+ sleep_for = min(poll_interval * (2 ** attempt), 30.0)
46
+ time.sleep(sleep_for)
47
+ attempt += 1
thrustlab/_http.py ADDED
@@ -0,0 +1,166 @@
1
+ """Internal HTTP transport for the ThrustLab SDK.
2
+
3
+ Wraps httpx.Client with auth, idempotency-key generation, retries with
4
+ exponential backoff + jitter, and error-envelope → exception mapping.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ import platform
11
+ import random
12
+ import time
13
+ import uuid
14
+ from typing import Any, Optional
15
+
16
+ import httpx
17
+
18
+ from thrustlab._version import __version__
19
+ from thrustlab.exceptions import (
20
+ APIError, NetworkError, RateLimitError, from_response_status,
21
+ )
22
+
23
+
24
+ logger = logging.getLogger("thrustlab")
25
+
26
+
27
+ def _user_agent() -> str:
28
+ return (
29
+ f"thrustlab-python/{__version__} "
30
+ f"(Python/{platform.python_version()}; {platform.system()})"
31
+ )
32
+
33
+
34
+ _RETRYABLE_STATUSES = {429, 500, 502, 503, 504}
35
+ _MUTATING_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
36
+
37
+
38
+ class Transport:
39
+ """HTTP transport for the SDK Client.
40
+
41
+ Responsibilities:
42
+ - inject Authorization + User-Agent on every request
43
+ - auto-generate Idempotency-Key on every mutating request unless caller passes one
44
+ - retry on 429 + 5xx + connection errors with exponential backoff + jitter
45
+ - map error responses to the right APIError subclass
46
+ - parse JSON responses
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ api_key: str,
52
+ base_url: str,
53
+ timeout: float = 30.0,
54
+ max_retries: int = 3,
55
+ client: Optional[httpx.Client] = None,
56
+ ) -> None:
57
+ self._api_key = api_key
58
+ self._base_url = base_url.rstrip("/")
59
+ self._max_retries = max_retries
60
+ self._client = client or httpx.Client(timeout=timeout)
61
+
62
+ def request(
63
+ self,
64
+ method: str,
65
+ path: str,
66
+ *,
67
+ params: Optional[dict[str, Any]] = None,
68
+ json: Optional[Any] = None,
69
+ idempotency_key: Optional[str] = None,
70
+ ) -> Any:
71
+ url = self._base_url + path
72
+ headers = {
73
+ "Authorization": f"Bearer {self._api_key}",
74
+ "User-Agent": _user_agent(),
75
+ "Accept": "application/json",
76
+ }
77
+ if json is not None:
78
+ headers["Content-Type"] = "application/json"
79
+ if method.upper() in _MUTATING_METHODS:
80
+ headers["Idempotency-Key"] = idempotency_key or str(uuid.uuid4())
81
+
82
+ last_exc: Optional[Exception] = None
83
+ for attempt in range(self._max_retries + 1):
84
+ try:
85
+ resp = self._client.request(method, url, params=params, json=json, headers=headers)
86
+ except httpx.HTTPError as exc:
87
+ net_err = NetworkError(f"network error: {exc}")
88
+ net_err.__cause__ = exc
89
+ last_exc = net_err
90
+ if attempt < self._max_retries:
91
+ self._sleep(attempt, retry_after=None)
92
+ continue
93
+ raise last_exc
94
+ else:
95
+ if resp.status_code in _RETRYABLE_STATUSES and attempt < self._max_retries:
96
+ retry_after = _parse_retry_after(resp)
97
+ self._sleep(attempt, retry_after=retry_after)
98
+ continue
99
+ return self._handle_response(resp)
100
+
101
+ # Unreachable in practice (loop returns or raises), but pacifies type checker.
102
+ if last_exc is not None:
103
+ raise last_exc
104
+ raise NetworkError("unreachable: exhausted retries with no exception")
105
+
106
+ def _sleep(self, attempt: int, retry_after: Optional[float]) -> None:
107
+ if retry_after is not None:
108
+ delay = retry_after
109
+ else:
110
+ base = 0.5 * (2 ** attempt)
111
+ jitter = random.uniform(0, 0.5)
112
+ delay = base + jitter
113
+ logger.debug("thrustlab: retrying after %.2fs (attempt %d)", delay, attempt + 1)
114
+ time.sleep(delay)
115
+
116
+ def _handle_response(self, resp: httpx.Response) -> Any:
117
+ if 200 <= resp.status_code < 300:
118
+ if resp.status_code == 204 or not resp.content:
119
+ return None
120
+ return resp.json()
121
+
122
+ # Error path. Parse envelope: { "error": { "type": ..., "code": ..., "message": ..., "param": ... } }.
123
+ request_id = resp.headers.get("X-Request-ID")
124
+ body: dict[str, Any] = {}
125
+ try:
126
+ body = resp.json()
127
+ except ValueError:
128
+ pass
129
+ env = body.get("error", {}) if isinstance(body, dict) else {}
130
+
131
+ kwargs = dict(
132
+ code=env.get("code"),
133
+ type=env.get("type"),
134
+ request_id=request_id,
135
+ http_status=resp.status_code,
136
+ param=env.get("param"),
137
+ )
138
+ message = env.get("message") or f"HTTP {resp.status_code}"
139
+
140
+ cls = from_response_status(resp.status_code)
141
+ if cls is RateLimitError:
142
+ return self._raise_rate_limit(message, kwargs, resp)
143
+ raise cls(message, **kwargs)
144
+
145
+ def _raise_rate_limit(self, message: str, kwargs: dict[str, Any], resp: httpx.Response) -> None:
146
+ retry_after = _parse_retry_after(resp)
147
+ raise RateLimitError(message, retry_after=retry_after, **kwargs)
148
+
149
+
150
+ def _parse_retry_after(resp: httpx.Response) -> Optional[float]:
151
+ raw = resp.headers.get("Retry-After")
152
+ if not raw:
153
+ return None
154
+ try:
155
+ return float(raw)
156
+ except (TypeError, ValueError):
157
+ pass
158
+ try:
159
+ from email.utils import parsedate_to_datetime
160
+ dt = parsedate_to_datetime(raw)
161
+ if dt is not None:
162
+ delta = dt.timestamp() - time.time()
163
+ return max(0.0, delta)
164
+ except (TypeError, ValueError):
165
+ pass
166
+ return None
@@ -0,0 +1,211 @@
1
+ """Contains all the data models used in inputs/outputs"""
2
+
3
+ from thrustlab._models.api_key import ApiKey
4
+ from thrustlab._models.api_key_create import ApiKeyCreate
5
+ from thrustlab._models.api_key_with_secret import ApiKeyWithSecret
6
+ from thrustlab._models.billing_portal_session_resource import BillingPortalSessionResource
7
+ from thrustlab._models.checkout_session_resource import CheckoutSessionResource
8
+ from thrustlab._models.component_brand import ComponentBrand
9
+ from thrustlab._models.component_brands_response import ComponentBrandsResponse
10
+ from thrustlab._models.component_create_body import ComponentCreateBody
11
+ from thrustlab._models.component_create_body_spec_json import ComponentCreateBodySpecJson
12
+ from thrustlab._models.component_list_response import ComponentListResponse
13
+ from thrustlab._models.component_patch import ComponentPatch
14
+ from thrustlab._models.component_patch_spec_json_type_0 import ComponentPatchSpecJsonType0
15
+ from thrustlab._models.component_resource import ComponentResource
16
+ from thrustlab._models.component_resource_spec_json import ComponentResourceSpecJson
17
+ from thrustlab._models.create_webhook_endpoint_v1_webhook_endpoints_post_response_create_webhook_endpoint_v1_webhook_endpoints_post import (
18
+ CreateWebhookEndpointV1WebhookEndpointsPostResponseCreateWebhookEndpointV1WebhookEndpointsPost,
19
+ )
20
+ from thrustlab._models.credit_balance_resource import CreditBalanceResource
21
+ from thrustlab._models.credit_bucket_breakdown import CreditBucketBreakdown
22
+ from thrustlab._models.credit_bucket_breakdown_type import CreditBucketBreakdownType
23
+ from thrustlab._models.credit_usage_event_resource import CreditUsageEventResource
24
+ from thrustlab._models.credit_usage_event_resource_credit_type import CreditUsageEventResourceCreditType
25
+ from thrustlab._models.credit_usage_event_resource_type import CreditUsageEventResourceType
26
+ from thrustlab._models.credit_usage_list_response import CreditUsageListResponse
27
+ from thrustlab._models.credit_usage_related_resource import CreditUsageRelatedResource
28
+ from thrustlab._models.credit_usage_related_resource_object import CreditUsageRelatedResourceObject
29
+ from thrustlab._models.fire_test_event_v1_webhook_endpoints_public_id_test_post_response_fire_test_event_v1_webhook_endpoints_public_id_test_post import (
30
+ FireTestEventV1WebhookEndpointsPublicIdTestPostResponseFireTestEventV1WebhookEndpointsPublicIdTestPost,
31
+ )
32
+ from thrustlab._models.get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get_response_get_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_get import (
33
+ GetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGetResponseGetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGet,
34
+ )
35
+ from thrustlab._models.get_event_v1_events_event_id_get_response_get_event_v1_events_event_id_get import (
36
+ GetEventV1EventsEventIdGetResponseGetEventV1EventsEventIdGet,
37
+ )
38
+ from thrustlab._models.get_webhook_endpoint_v1_webhook_endpoints_public_id_get_response_get_webhook_endpoint_v1_webhook_endpoints_public_id_get import (
39
+ GetWebhookEndpointV1WebhookEndpointsPublicIdGetResponseGetWebhookEndpointV1WebhookEndpointsPublicIdGet,
40
+ )
41
+ from thrustlab._models.http_validation_error import HTTPValidationError
42
+ from thrustlab._models.list_deliveries_v1_webhook_endpoints_public_id_deliveries_get_response_list_deliveries_v1_webhook_endpoints_public_id_deliveries_get import (
43
+ ListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGetResponseListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGet,
44
+ )
45
+ from thrustlab._models.list_events_v1_events_get_response_list_events_v1_events_get import (
46
+ ListEventsV1EventsGetResponseListEventsV1EventsGet,
47
+ )
48
+ from thrustlab._models.list_response_api_key import ListResponseApiKey
49
+ from thrustlab._models.list_webhook_endpoints_v1_webhook_endpoints_get_response_list_webhook_endpoints_v1_webhook_endpoints_get import (
50
+ ListWebhookEndpointsV1WebhookEndpointsGetResponseListWebhookEndpointsV1WebhookEndpointsGet,
51
+ )
52
+ from thrustlab._models.patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch_response_patch_webhook_endpoint_v1_webhook_endpoints_public_id_patch import (
53
+ PatchWebhookEndpointV1WebhookEndpointsPublicIdPatchResponsePatchWebhookEndpointV1WebhookEndpointsPublicIdPatch,
54
+ )
55
+ from thrustlab._models.portal_session_in import PortalSessionIn
56
+ from thrustlab._models.project_create_body import ProjectCreateBody
57
+ from thrustlab._models.project_list_response import ProjectListResponse
58
+ from thrustlab._models.project_patch import ProjectPatch
59
+ from thrustlab._models.project_resource import ProjectResource
60
+ from thrustlab._models.retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post_response_retry_delivery_v1_webhook_endpoints_public_id_deliveries_delivery_public_id_retry_post import (
61
+ RetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPostResponseRetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPost,
62
+ )
63
+ from thrustlab._models.rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post_response_rotate_webhook_secret_v1_webhook_endpoints_public_id_rotate_secret_post import (
64
+ RotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPostResponseRotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPost,
65
+ )
66
+ from thrustlab._models.rotor_group_in import RotorGroupIn
67
+ from thrustlab._models.rotor_group_resource import RotorGroupResource
68
+ from thrustlab._models.simulation_cancel_body import SimulationCancelBody
69
+ from thrustlab._models.simulation_create_body import SimulationCreateBody
70
+ from thrustlab._models.simulation_inputs import SimulationInputs
71
+ from thrustlab._models.simulation_list_response import SimulationListResponse
72
+ from thrustlab._models.simulation_patch import SimulationPatch
73
+ from thrustlab._models.simulation_patch_plot_config_json_type_0_item import SimulationPatchPlotConfigJsonType0Item
74
+ from thrustlab._models.simulation_resource import SimulationResource
75
+ from thrustlab._models.simulation_resource_error_type_0 import SimulationResourceErrorType0
76
+ from thrustlab._models.simulation_resource_plot_config_json_type_0_item import SimulationResourcePlotConfigJsonType0Item
77
+ from thrustlab._models.simulation_resource_result_type_0 import SimulationResourceResultType0
78
+ from thrustlab._models.starred_component_create_body import StarredComponentCreateBody
79
+ from thrustlab._models.starred_component_list_response import StarredComponentListResponse
80
+ from thrustlab._models.starred_component_resource import StarredComponentResource
81
+ from thrustlab._models.submission_create_body import SubmissionCreateBody
82
+ from thrustlab._models.submission_create_body_data_json import SubmissionCreateBodyDataJson
83
+ from thrustlab._models.submission_list_response import SubmissionListResponse
84
+ from thrustlab._models.submission_patch import SubmissionPatch
85
+ from thrustlab._models.submission_patch_data_json_type_0 import SubmissionPatchDataJsonType0
86
+ from thrustlab._models.submission_resource import SubmissionResource
87
+ from thrustlab._models.submission_resource_data_json import SubmissionResourceDataJson
88
+ from thrustlab._models.subscription_create import SubscriptionCreate
89
+ from thrustlab._models.subscription_resource import SubscriptionResource
90
+ from thrustlab._models.subscription_resource_status import SubscriptionResourceStatus
91
+ from thrustlab._models.subscription_resource_tier import SubscriptionResourceTier
92
+ from thrustlab._models.sweep_cancel_body import SweepCancelBody
93
+ from thrustlab._models.sweep_config_in import SweepConfigIn
94
+ from thrustlab._models.sweep_create_body import SweepCreateBody
95
+ from thrustlab._models.sweep_inputs import SweepInputs
96
+ from thrustlab._models.sweep_list_response import SweepListResponse
97
+ from thrustlab._models.sweep_param_range_in import SweepParamRangeIn
98
+ from thrustlab._models.sweep_param_range_in_mode import SweepParamRangeInMode
99
+ from thrustlab._models.sweep_patch import SweepPatch
100
+ from thrustlab._models.sweep_patch_plot_config_json_type_0_item import SweepPatchPlotConfigJsonType0Item
101
+ from thrustlab._models.sweep_point_list_response import SweepPointListResponse
102
+ from thrustlab._models.sweep_point_resource import SweepPointResource
103
+ from thrustlab._models.sweep_point_resource_inputs import SweepPointResourceInputs
104
+ from thrustlab._models.sweep_point_resource_rotors_type_0 import SweepPointResourceRotorsType0
105
+ from thrustlab._models.sweep_resource import SweepResource
106
+ from thrustlab._models.sweep_resource_error_type_0 import SweepResourceErrorType0
107
+ from thrustlab._models.sweep_resource_plot_config_json_type_0_item import SweepResourcePlotConfigJsonType0Item
108
+ from thrustlab._models.sweep_resource_summary_type_0 import SweepResourceSummaryType0
109
+ from thrustlab._models.sweep_resource_sweep_config_type_0 import SweepResourceSweepConfigType0
110
+ from thrustlab._models.sweep_rotor_group_in import SweepRotorGroupIn
111
+ from thrustlab._models.user_patch import UserPatch
112
+ from thrustlab._models.user_resource import UserResource
113
+ from thrustlab._models.v1_health_v1_health_get_response_v1_health_v1_health_get import V1HealthV1HealthGetResponseV1HealthV1HealthGet
114
+ from thrustlab._models.validation_error import ValidationError
115
+ from thrustlab._models.webhook_endpoint_create import WebhookEndpointCreate
116
+ from thrustlab._models.webhook_endpoint_patch import WebhookEndpointPatch
117
+
118
+ __all__ = (
119
+ "ApiKey",
120
+ "ApiKeyCreate",
121
+ "ApiKeyWithSecret",
122
+ "BillingPortalSessionResource",
123
+ "CheckoutSessionResource",
124
+ "ComponentBrand",
125
+ "ComponentBrandsResponse",
126
+ "ComponentCreateBody",
127
+ "ComponentCreateBodySpecJson",
128
+ "ComponentListResponse",
129
+ "ComponentPatch",
130
+ "ComponentPatchSpecJsonType0",
131
+ "ComponentResource",
132
+ "ComponentResourceSpecJson",
133
+ "CreateWebhookEndpointV1WebhookEndpointsPostResponseCreateWebhookEndpointV1WebhookEndpointsPost",
134
+ "CreditBalanceResource",
135
+ "CreditBucketBreakdown",
136
+ "CreditBucketBreakdownType",
137
+ "CreditUsageEventResource",
138
+ "CreditUsageEventResourceCreditType",
139
+ "CreditUsageEventResourceType",
140
+ "CreditUsageListResponse",
141
+ "CreditUsageRelatedResource",
142
+ "CreditUsageRelatedResourceObject",
143
+ "FireTestEventV1WebhookEndpointsPublicIdTestPostResponseFireTestEventV1WebhookEndpointsPublicIdTestPost",
144
+ "GetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGetResponseGetDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdGet",
145
+ "GetEventV1EventsEventIdGetResponseGetEventV1EventsEventIdGet",
146
+ "GetWebhookEndpointV1WebhookEndpointsPublicIdGetResponseGetWebhookEndpointV1WebhookEndpointsPublicIdGet",
147
+ "HTTPValidationError",
148
+ "ListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGetResponseListDeliveriesV1WebhookEndpointsPublicIdDeliveriesGet",
149
+ "ListEventsV1EventsGetResponseListEventsV1EventsGet",
150
+ "ListResponseApiKey",
151
+ "ListWebhookEndpointsV1WebhookEndpointsGetResponseListWebhookEndpointsV1WebhookEndpointsGet",
152
+ "PatchWebhookEndpointV1WebhookEndpointsPublicIdPatchResponsePatchWebhookEndpointV1WebhookEndpointsPublicIdPatch",
153
+ "PortalSessionIn",
154
+ "ProjectCreateBody",
155
+ "ProjectListResponse",
156
+ "ProjectPatch",
157
+ "ProjectResource",
158
+ "RetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPostResponseRetryDeliveryV1WebhookEndpointsPublicIdDeliveriesDeliveryPublicIdRetryPost",
159
+ "RotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPostResponseRotateWebhookSecretV1WebhookEndpointsPublicIdRotateSecretPost",
160
+ "RotorGroupIn",
161
+ "RotorGroupResource",
162
+ "SimulationCancelBody",
163
+ "SimulationCreateBody",
164
+ "SimulationInputs",
165
+ "SimulationListResponse",
166
+ "SimulationPatch",
167
+ "SimulationPatchPlotConfigJsonType0Item",
168
+ "SimulationResource",
169
+ "SimulationResourceErrorType0",
170
+ "SimulationResourcePlotConfigJsonType0Item",
171
+ "SimulationResourceResultType0",
172
+ "StarredComponentCreateBody",
173
+ "StarredComponentListResponse",
174
+ "StarredComponentResource",
175
+ "SubmissionCreateBody",
176
+ "SubmissionCreateBodyDataJson",
177
+ "SubmissionListResponse",
178
+ "SubmissionPatch",
179
+ "SubmissionPatchDataJsonType0",
180
+ "SubmissionResource",
181
+ "SubmissionResourceDataJson",
182
+ "SubscriptionCreate",
183
+ "SubscriptionResource",
184
+ "SubscriptionResourceStatus",
185
+ "SubscriptionResourceTier",
186
+ "SweepCancelBody",
187
+ "SweepConfigIn",
188
+ "SweepCreateBody",
189
+ "SweepInputs",
190
+ "SweepListResponse",
191
+ "SweepParamRangeIn",
192
+ "SweepParamRangeInMode",
193
+ "SweepPatch",
194
+ "SweepPatchPlotConfigJsonType0Item",
195
+ "SweepPointListResponse",
196
+ "SweepPointResource",
197
+ "SweepPointResourceInputs",
198
+ "SweepPointResourceRotorsType0",
199
+ "SweepResource",
200
+ "SweepResourceErrorType0",
201
+ "SweepResourcePlotConfigJsonType0Item",
202
+ "SweepResourceSummaryType0",
203
+ "SweepResourceSweepConfigType0",
204
+ "SweepRotorGroupIn",
205
+ "UserPatch",
206
+ "UserResource",
207
+ "V1HealthV1HealthGetResponseV1HealthV1HealthGet",
208
+ "ValidationError",
209
+ "WebhookEndpointCreate",
210
+ "WebhookEndpointPatch",
211
+ )
@@ -0,0 +1,184 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+ from collections.abc import Mapping
5
+ from typing import Any, Literal, TypeVar, cast
6
+
7
+ from attrs import define as _attrs_define
8
+ from attrs import field as _attrs_field
9
+ from dateutil.parser import isoparse
10
+
11
+ from thrustlab._models.types import UNSET, Unset
12
+
13
+ T = TypeVar("T", bound="ApiKey")
14
+
15
+
16
+ @_attrs_define
17
+ class ApiKey:
18
+ """API key resource as returned by LIST and GET.
19
+
20
+ NEVER includes the raw ``key`` field — that field only appears in the
21
+ POST response body via :class:`ApiKeyWithSecret` below.
22
+
23
+ Attributes:
24
+ created_at (datetime.datetime):
25
+ id (str):
26
+ key_prefix (str):
27
+ key_suffix (str):
28
+ last_used_at (datetime.datetime | None | Unset):
29
+ name (None | str | Unset):
30
+ object_ (Literal['api_key'] | Unset): Default: 'api_key'.
31
+ revoked_at (datetime.datetime | None | Unset):
32
+ """
33
+
34
+ created_at: datetime.datetime
35
+ id: str
36
+ key_prefix: str
37
+ key_suffix: str
38
+ last_used_at: datetime.datetime | None | Unset = UNSET
39
+ name: None | str | Unset = UNSET
40
+ object_: Literal["api_key"] | Unset = "api_key"
41
+ revoked_at: datetime.datetime | None | Unset = UNSET
42
+ additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)
43
+
44
+ def to_dict(self) -> dict[str, Any]:
45
+ created_at = self.created_at.isoformat()
46
+
47
+ id = self.id
48
+
49
+ key_prefix = self.key_prefix
50
+
51
+ key_suffix = self.key_suffix
52
+
53
+ last_used_at: None | str | Unset
54
+ if isinstance(self.last_used_at, Unset):
55
+ last_used_at = UNSET
56
+ elif isinstance(self.last_used_at, datetime.datetime):
57
+ last_used_at = self.last_used_at.isoformat()
58
+ else:
59
+ last_used_at = self.last_used_at
60
+
61
+ name: None | str | Unset
62
+ if isinstance(self.name, Unset):
63
+ name = UNSET
64
+ else:
65
+ name = self.name
66
+
67
+ object_ = self.object_
68
+
69
+ revoked_at: None | str | Unset
70
+ if isinstance(self.revoked_at, Unset):
71
+ revoked_at = UNSET
72
+ elif isinstance(self.revoked_at, datetime.datetime):
73
+ revoked_at = self.revoked_at.isoformat()
74
+ else:
75
+ revoked_at = self.revoked_at
76
+
77
+ field_dict: dict[str, Any] = {}
78
+ field_dict.update(self.additional_properties)
79
+ field_dict.update(
80
+ {
81
+ "created_at": created_at,
82
+ "id": id,
83
+ "key_prefix": key_prefix,
84
+ "key_suffix": key_suffix,
85
+ }
86
+ )
87
+ if last_used_at is not UNSET:
88
+ field_dict["last_used_at"] = last_used_at
89
+ if name is not UNSET:
90
+ field_dict["name"] = name
91
+ if object_ is not UNSET:
92
+ field_dict["object"] = object_
93
+ if revoked_at is not UNSET:
94
+ field_dict["revoked_at"] = revoked_at
95
+
96
+ return field_dict
97
+
98
+ @classmethod
99
+ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
100
+ d = dict(src_dict)
101
+ created_at = isoparse(d.pop("created_at"))
102
+
103
+ id = d.pop("id")
104
+
105
+ key_prefix = d.pop("key_prefix")
106
+
107
+ key_suffix = d.pop("key_suffix")
108
+
109
+ def _parse_last_used_at(data: object) -> datetime.datetime | None | Unset:
110
+ if data is None:
111
+ return data
112
+ if isinstance(data, Unset):
113
+ return data
114
+ try:
115
+ if not isinstance(data, str):
116
+ raise TypeError()
117
+ last_used_at_type_0 = isoparse(data)
118
+
119
+ return last_used_at_type_0
120
+ except (TypeError, ValueError, AttributeError, KeyError):
121
+ pass
122
+ return cast(datetime.datetime | None | Unset, data)
123
+
124
+ last_used_at = _parse_last_used_at(d.pop("last_used_at", UNSET))
125
+
126
+ def _parse_name(data: object) -> None | str | Unset:
127
+ if data is None:
128
+ return data
129
+ if isinstance(data, Unset):
130
+ return data
131
+ return cast(None | str | Unset, data)
132
+
133
+ name = _parse_name(d.pop("name", UNSET))
134
+
135
+ object_ = cast(Literal["api_key"] | Unset, d.pop("object", UNSET))
136
+ if object_ != "api_key" and not isinstance(object_, Unset):
137
+ raise ValueError(f"object must match const 'api_key', got '{object_}'")
138
+
139
+ def _parse_revoked_at(data: object) -> datetime.datetime | None | Unset:
140
+ if data is None:
141
+ return data
142
+ if isinstance(data, Unset):
143
+ return data
144
+ try:
145
+ if not isinstance(data, str):
146
+ raise TypeError()
147
+ revoked_at_type_0 = isoparse(data)
148
+
149
+ return revoked_at_type_0
150
+ except (TypeError, ValueError, AttributeError, KeyError):
151
+ pass
152
+ return cast(datetime.datetime | None | Unset, data)
153
+
154
+ revoked_at = _parse_revoked_at(d.pop("revoked_at", UNSET))
155
+
156
+ api_key = cls(
157
+ created_at=created_at,
158
+ id=id,
159
+ key_prefix=key_prefix,
160
+ key_suffix=key_suffix,
161
+ last_used_at=last_used_at,
162
+ name=name,
163
+ object_=object_,
164
+ revoked_at=revoked_at,
165
+ )
166
+
167
+ api_key.additional_properties = d
168
+ return api_key
169
+
170
+ @property
171
+ def additional_keys(self) -> list[str]:
172
+ return list(self.additional_properties.keys())
173
+
174
+ def __getitem__(self, key: str) -> Any:
175
+ return self.additional_properties[key]
176
+
177
+ def __setitem__(self, key: str, value: Any) -> None:
178
+ self.additional_properties[key] = value
179
+
180
+ def __delitem__(self, key: str) -> None:
181
+ del self.additional_properties[key]
182
+
183
+ def __contains__(self, key: str) -> bool:
184
+ return key in self.additional_properties