redblack-python-sdk 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.
- redblack/__init__.py +105 -0
- redblack/_generated/__init__.py +39 -0
- redblack/_generated/contract.py +212 -0
- redblack/_generated/operation_specs.py +5115 -0
- redblack/_version.py +4 -0
- redblack/auth.py +352 -0
- redblack/client.py +512 -0
- redblack/config.py +576 -0
- redblack/downloads.py +410 -0
- redblack/errors.py +483 -0
- redblack/models/__init__.py +105 -0
- redblack/models/base.py +150 -0
- redblack/models/generated.py +11736 -0
- redblack/odata.py +75 -0
- redblack/pagination.py +184 -0
- redblack/polling.py +163 -0
- redblack/py.typed +1 -0
- redblack/raw.py +102 -0
- redblack/resources/__init__.py +65 -0
- redblack/resources/_base.py +212 -0
- redblack/resources/_collections.py +80 -0
- redblack/resources/_exports.py +215 -0
- redblack/resources/accounts.py +132 -0
- redblack/resources/cash_actions.py +260 -0
- redblack/resources/equivalents.py +144 -0
- redblack/resources/households.py +138 -0
- redblack/resources/orders.py +118 -0
- redblack/resources/restrictions.py +146 -0
- redblack/resources/review_sets.py +889 -0
- redblack/resources/securities.py +175 -0
- redblack/resources/tasks.py +291 -0
- redblack/resources/users.py +88 -0
- redblack/response.py +118 -0
- redblack/transport.py +606 -0
- redblack_python_sdk-0.1.0.dist-info/METADATA +151 -0
- redblack_python_sdk-0.1.0.dist-info/RECORD +38 -0
- redblack_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- redblack_python_sdk-0.1.0.dist-info/top_level.txt +1 -0
redblack/errors.py
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"""Public exception hierarchy and defensive API-error parsing."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from collections.abc import Iterable, Mapping
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from types import MappingProxyType
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
import httpx
|
|
15
|
+
|
|
16
|
+
_MAX_ERROR_BODY = 65_536
|
|
17
|
+
_SENSITIVE_HEADERS = {
|
|
18
|
+
"authorization",
|
|
19
|
+
"client-id",
|
|
20
|
+
"cookie",
|
|
21
|
+
"proxy-authorization",
|
|
22
|
+
"set-cookie",
|
|
23
|
+
"x-api-key",
|
|
24
|
+
}
|
|
25
|
+
_REQUEST_ID_HEADERS = (
|
|
26
|
+
"x-request-id",
|
|
27
|
+
"request-id",
|
|
28
|
+
"x-correlation-id",
|
|
29
|
+
"traceparent",
|
|
30
|
+
)
|
|
31
|
+
_SENSITIVE_ASSIGNMENT = re.compile(
|
|
32
|
+
r"(?i)(authorization|client[_-]?secret|password|access[_-]?token|"
|
|
33
|
+
r"x-api-key|api[_-]?key)\s*([:=]\s*|[\"']\s*:\s*[\"'])"
|
|
34
|
+
r"([^,\s&\"']+)"
|
|
35
|
+
)
|
|
36
|
+
_BEARER_VALUE = re.compile(r"(?i)\bBearer\s+[A-Za-z0-9._~+/=-]+")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class RedBlackError(Exception):
|
|
40
|
+
"""Base class for all SDK-owned exceptions."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ConfigurationError(RedBlackError):
|
|
44
|
+
"""The SDK configuration is missing, ambiguous, or unsafe."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AuthenticationError(RedBlackError):
|
|
48
|
+
"""Token acquisition or a custom token provider failed."""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
message: str = "RedBlack authentication failed",
|
|
53
|
+
*,
|
|
54
|
+
error: str | None = None,
|
|
55
|
+
error_description: str | None = None,
|
|
56
|
+
status_code: int | None = None,
|
|
57
|
+
request_id: str | None = None,
|
|
58
|
+
secrets: Iterable[str] = (),
|
|
59
|
+
) -> None:
|
|
60
|
+
self.error = redact_text(error, secrets=secrets) if error else None
|
|
61
|
+
self.error_description = (
|
|
62
|
+
redact_text(error_description, secrets=secrets) if error_description else None
|
|
63
|
+
)
|
|
64
|
+
self.status_code = status_code
|
|
65
|
+
self.request_id = request_id
|
|
66
|
+
super().__init__(redact_text(message, secrets=secrets))
|
|
67
|
+
|
|
68
|
+
def __repr__(self) -> str:
|
|
69
|
+
return (
|
|
70
|
+
f"{type(self).__name__}(status_code={self.status_code!r}, "
|
|
71
|
+
f"error={self.error!r}, request_id={self.request_id!r})"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TransportError(RedBlackError):
|
|
76
|
+
"""The request did not produce an HTTP response."""
|
|
77
|
+
|
|
78
|
+
def __init__(
|
|
79
|
+
self,
|
|
80
|
+
message: str,
|
|
81
|
+
*,
|
|
82
|
+
method: str | None = None,
|
|
83
|
+
path: str | None = None,
|
|
84
|
+
) -> None:
|
|
85
|
+
self.method = method
|
|
86
|
+
self.path = path
|
|
87
|
+
context = _format_request_context(method, path)
|
|
88
|
+
super().__init__(f"{message}{context}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class RequestTimeoutError(TransportError):
|
|
92
|
+
"""An HTTP request timed out."""
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass(frozen=True, slots=True, repr=False)
|
|
96
|
+
class ProblemDetails:
|
|
97
|
+
"""Tolerant representation of an RFC-style problem response."""
|
|
98
|
+
|
|
99
|
+
type: str | None = None
|
|
100
|
+
title: str | None = None
|
|
101
|
+
status: int | None = None
|
|
102
|
+
detail: str | None = field(default=None, repr=False)
|
|
103
|
+
instance: str | None = field(default=None, repr=False)
|
|
104
|
+
extensions: Mapping[str, Any] = field(default_factory=lambda: MappingProxyType({}), repr=False)
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def from_mapping(cls, value: Mapping[str, Any]) -> ProblemDetails:
|
|
108
|
+
known = {"type", "title", "status", "detail", "instance"}
|
|
109
|
+
raw_status = value.get("status")
|
|
110
|
+
status = (
|
|
111
|
+
raw_status if isinstance(raw_status, int) and not isinstance(raw_status, bool) else None
|
|
112
|
+
)
|
|
113
|
+
return cls(
|
|
114
|
+
type=_optional_string(value.get("type")),
|
|
115
|
+
title=_optional_string(value.get("title")),
|
|
116
|
+
status=status,
|
|
117
|
+
detail=_optional_string(value.get("detail")),
|
|
118
|
+
instance=_optional_string(value.get("instance")),
|
|
119
|
+
extensions=MappingProxyType(
|
|
120
|
+
{key: item for key, item in value.items() if key not in known}
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def __repr__(self) -> str:
|
|
125
|
+
return (
|
|
126
|
+
"ProblemDetails("
|
|
127
|
+
f"has_type={self.type is not None}, "
|
|
128
|
+
f"has_title={self.title is not None}, status={self.status!r}, "
|
|
129
|
+
f"has_detail={self.detail is not None}, "
|
|
130
|
+
f"extension_count={len(self.extensions)!r})"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class ApiError(RedBlackError):
|
|
135
|
+
"""A RedBlack resource endpoint returned an unsuccessful response."""
|
|
136
|
+
|
|
137
|
+
def __init__(
|
|
138
|
+
self,
|
|
139
|
+
*,
|
|
140
|
+
status_code: int,
|
|
141
|
+
method: str,
|
|
142
|
+
path: str,
|
|
143
|
+
problem: ProblemDetails | None = None,
|
|
144
|
+
structured_errors: Any = None,
|
|
145
|
+
unknown_fields: Mapping[str, Any] | None = None,
|
|
146
|
+
headers: Mapping[str, str] | None = None,
|
|
147
|
+
request_id: str | None = None,
|
|
148
|
+
raw_body: bytes = b"",
|
|
149
|
+
body_truncated: bool = False,
|
|
150
|
+
) -> None:
|
|
151
|
+
self.status_code = status_code
|
|
152
|
+
self.method = method.upper()
|
|
153
|
+
self.path = path
|
|
154
|
+
self.problem = problem
|
|
155
|
+
self.structured_errors = structured_errors
|
|
156
|
+
self.unknown_fields = MappingProxyType(dict(unknown_fields or {}))
|
|
157
|
+
self.headers = MappingProxyType(dict(headers or {}))
|
|
158
|
+
self.request_id = request_id
|
|
159
|
+
self.raw_body = raw_body
|
|
160
|
+
self.body_truncated = body_truncated
|
|
161
|
+
message = (
|
|
162
|
+
f"RedBlack API request failed with HTTP {status_code}"
|
|
163
|
+
f"{_format_request_context(self.method, self.path)}"
|
|
164
|
+
)
|
|
165
|
+
if request_id:
|
|
166
|
+
message += f" [request_id={request_id}]"
|
|
167
|
+
super().__init__(message)
|
|
168
|
+
|
|
169
|
+
def __repr__(self) -> str:
|
|
170
|
+
return (
|
|
171
|
+
f"{type(self).__name__}(status_code={self.status_code!r}, "
|
|
172
|
+
f"method={self.method!r}, path={self.path!r}, "
|
|
173
|
+
f"request_id={self.request_id!r}, "
|
|
174
|
+
f"has_problem={self.problem is not None}, "
|
|
175
|
+
f"raw_body_bytes={len(self.raw_body)!r})"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class BadRequestError(ApiError):
|
|
180
|
+
"""HTTP 400."""
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class UnauthorizedError(ApiError):
|
|
184
|
+
"""HTTP 401 from a resource endpoint."""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class ForbiddenError(ApiError):
|
|
188
|
+
"""HTTP 403."""
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class NotFoundError(ApiError):
|
|
192
|
+
"""HTTP 404 (not found or not visible to this API user)."""
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class MethodNotAllowedError(ApiError):
|
|
196
|
+
"""HTTP 405."""
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class ConflictError(ApiError):
|
|
200
|
+
"""HTTP 409."""
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class GoneError(ApiError):
|
|
204
|
+
"""HTTP 410."""
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class PreconditionFailedError(ApiError):
|
|
208
|
+
"""HTTP 412, usually an ETag mismatch."""
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class UnprocessableContentError(ApiError):
|
|
212
|
+
"""HTTP 422."""
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class RateLimitError(ApiError):
|
|
216
|
+
"""HTTP 429."""
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def retry_after(self) -> str | None:
|
|
220
|
+
return self.headers.get("retry-after")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class ServerError(ApiError):
|
|
224
|
+
"""HTTP 5xx."""
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class ResponseDecodeError(RedBlackError):
|
|
228
|
+
"""A nominally successful response could not be decoded as contracted."""
|
|
229
|
+
|
|
230
|
+
def __init__(
|
|
231
|
+
self,
|
|
232
|
+
message: str,
|
|
233
|
+
*,
|
|
234
|
+
status_code: int | None = None,
|
|
235
|
+
method: str | None = None,
|
|
236
|
+
path: str | None = None,
|
|
237
|
+
request_id: str | None = None,
|
|
238
|
+
raw_body: bytes = b"",
|
|
239
|
+
) -> None:
|
|
240
|
+
self.status_code = status_code
|
|
241
|
+
self.method = method
|
|
242
|
+
self.path = path
|
|
243
|
+
self.request_id = request_id
|
|
244
|
+
self.raw_body = bytes(raw_body[:_MAX_ERROR_BODY])
|
|
245
|
+
self.body_truncated = len(raw_body) > len(self.raw_body)
|
|
246
|
+
rendered = f"{message}{_format_request_context(method, path)}"
|
|
247
|
+
if request_id:
|
|
248
|
+
rendered += f" [request_id={request_id}]"
|
|
249
|
+
super().__init__(rendered)
|
|
250
|
+
|
|
251
|
+
def __repr__(self) -> str:
|
|
252
|
+
return (
|
|
253
|
+
f"{type(self).__name__}(status_code={self.status_code!r}, "
|
|
254
|
+
f"method={self.method!r}, path={self.path!r}, "
|
|
255
|
+
f"request_id={self.request_id!r}, "
|
|
256
|
+
f"raw_body_bytes={len(self.raw_body)!r}, "
|
|
257
|
+
f"body_truncated={self.body_truncated!r})"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
class PollingError(RedBlackError):
|
|
262
|
+
"""Base class for local polling and terminal workflow failures."""
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class PollTimeoutError(PollingError):
|
|
266
|
+
"""A local polling deadline elapsed."""
|
|
267
|
+
|
|
268
|
+
timeout: float | None
|
|
269
|
+
last_value: object | None
|
|
270
|
+
|
|
271
|
+
def __init__(
|
|
272
|
+
self,
|
|
273
|
+
message_or_timeout: str | float | None,
|
|
274
|
+
last_value: object | None = None,
|
|
275
|
+
*,
|
|
276
|
+
timeout: float | None = None,
|
|
277
|
+
) -> None:
|
|
278
|
+
if isinstance(message_or_timeout, str):
|
|
279
|
+
message = message_or_timeout
|
|
280
|
+
resolved_timeout = timeout
|
|
281
|
+
else:
|
|
282
|
+
resolved_timeout = message_or_timeout if timeout is None else timeout
|
|
283
|
+
message = f"workflow did not finish within {resolved_timeout!r} seconds"
|
|
284
|
+
self.timeout = resolved_timeout
|
|
285
|
+
self.last_value = last_value
|
|
286
|
+
super().__init__(message)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class OperationFailedError(PollingError):
|
|
290
|
+
"""A server-side long-running operation reached a failed terminal state."""
|
|
291
|
+
|
|
292
|
+
def __init__(self, message: str, *, final_state: Any = None) -> None:
|
|
293
|
+
self.final_state = final_state
|
|
294
|
+
super().__init__(message)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class OperationNeedsInputError(PollingError):
|
|
298
|
+
"""A workflow stopped in a state that requires user action."""
|
|
299
|
+
|
|
300
|
+
def __init__(self, message: str, *, final_state: Any = None) -> None:
|
|
301
|
+
self.final_state = final_state
|
|
302
|
+
super().__init__(message)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
_STATUS_ERRORS: dict[int, type[ApiError]] = {
|
|
306
|
+
400: BadRequestError,
|
|
307
|
+
401: UnauthorizedError,
|
|
308
|
+
403: ForbiddenError,
|
|
309
|
+
404: NotFoundError,
|
|
310
|
+
405: MethodNotAllowedError,
|
|
311
|
+
409: ConflictError,
|
|
312
|
+
410: GoneError,
|
|
313
|
+
412: PreconditionFailedError,
|
|
314
|
+
422: UnprocessableContentError,
|
|
315
|
+
429: RateLimitError,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def api_error_from_response(
|
|
320
|
+
response: httpx.Response,
|
|
321
|
+
*,
|
|
322
|
+
method: str | None = None,
|
|
323
|
+
path: str | None = None,
|
|
324
|
+
) -> ApiError:
|
|
325
|
+
"""Parse a resource response without trusting its Content-Type header."""
|
|
326
|
+
|
|
327
|
+
request_method = method or str(response.request.method)
|
|
328
|
+
request_path = path or response.request.url.path
|
|
329
|
+
original = response.content
|
|
330
|
+
bounded = original[:_MAX_ERROR_BODY]
|
|
331
|
+
body_truncated = len(original) > len(bounded)
|
|
332
|
+
|
|
333
|
+
payload: Any = None
|
|
334
|
+
if original:
|
|
335
|
+
try:
|
|
336
|
+
payload = json.loads(original)
|
|
337
|
+
except (UnicodeDecodeError, json.JSONDecodeError):
|
|
338
|
+
payload = None
|
|
339
|
+
|
|
340
|
+
problem: ProblemDetails | None = None
|
|
341
|
+
structured_errors: Any = None
|
|
342
|
+
unknown_fields: dict[str, Any] = {}
|
|
343
|
+
if isinstance(payload, Mapping):
|
|
344
|
+
problem_keys = {"type", "title", "status", "detail", "instance"}
|
|
345
|
+
if problem_keys.intersection(payload):
|
|
346
|
+
problem = ProblemDetails.from_mapping(payload)
|
|
347
|
+
structured_errors = (
|
|
348
|
+
payload.get("validationErrors")
|
|
349
|
+
if "validationErrors" in payload
|
|
350
|
+
else payload.get("errors")
|
|
351
|
+
)
|
|
352
|
+
recognized = problem_keys | {"errors", "validationErrors"}
|
|
353
|
+
unknown_fields = {
|
|
354
|
+
str(key): value for key, value in payload.items() if key not in recognized
|
|
355
|
+
}
|
|
356
|
+
elif payload is not None:
|
|
357
|
+
unknown_fields = {"payload": payload}
|
|
358
|
+
|
|
359
|
+
headers = safe_response_headers(response.headers)
|
|
360
|
+
request_id = extract_request_id(response.headers)
|
|
361
|
+
error_class = (
|
|
362
|
+
ServerError
|
|
363
|
+
if 500 <= response.status_code <= 599
|
|
364
|
+
else _STATUS_ERRORS.get(response.status_code, ApiError)
|
|
365
|
+
)
|
|
366
|
+
return error_class(
|
|
367
|
+
status_code=response.status_code,
|
|
368
|
+
method=request_method,
|
|
369
|
+
path=request_path,
|
|
370
|
+
problem=problem,
|
|
371
|
+
structured_errors=structured_errors,
|
|
372
|
+
unknown_fields=unknown_fields,
|
|
373
|
+
headers=headers,
|
|
374
|
+
request_id=request_id,
|
|
375
|
+
raw_body=bounded,
|
|
376
|
+
body_truncated=body_truncated,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def extract_request_id(headers: Mapping[str, str]) -> str | None:
|
|
381
|
+
"""Return the first recognized server correlation header."""
|
|
382
|
+
|
|
383
|
+
lowered = {key.lower(): value for key, value in headers.items()}
|
|
384
|
+
for name in _REQUEST_ID_HEADERS:
|
|
385
|
+
if value := lowered.get(name):
|
|
386
|
+
return value
|
|
387
|
+
return None
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def safe_response_headers(headers: Mapping[str, str]) -> dict[str, str]:
|
|
391
|
+
"""Copy response headers while removing credential-bearing values."""
|
|
392
|
+
|
|
393
|
+
safe: dict[str, str] = {}
|
|
394
|
+
for name, value in headers.items():
|
|
395
|
+
lowered = name.lower()
|
|
396
|
+
if lowered in _SENSITIVE_HEADERS:
|
|
397
|
+
continue
|
|
398
|
+
safe[lowered] = sanitize_url(value) if lowered == "location" else redact_text(value)
|
|
399
|
+
return safe
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def sanitize_url(value: str) -> str:
|
|
403
|
+
"""Remove user information, query values, and fragments from a URL."""
|
|
404
|
+
|
|
405
|
+
try:
|
|
406
|
+
split = urlsplit(value)
|
|
407
|
+
except ValueError:
|
|
408
|
+
return "<redacted-url>"
|
|
409
|
+
if not split.scheme and not split.netloc:
|
|
410
|
+
return urlunsplit(("", "", split.path, "", ""))
|
|
411
|
+
hostname = split.hostname or ""
|
|
412
|
+
if ":" in hostname and not hostname.startswith("["):
|
|
413
|
+
hostname = f"[{hostname}]"
|
|
414
|
+
netloc = hostname
|
|
415
|
+
try:
|
|
416
|
+
port = split.port
|
|
417
|
+
except ValueError:
|
|
418
|
+
return "<redacted-url>"
|
|
419
|
+
if port is not None:
|
|
420
|
+
netloc += f":{port}"
|
|
421
|
+
return urlunsplit((split.scheme, netloc, split.path, "", ""))
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def redact_text(value: str | None, *, secrets: Iterable[str] = ()) -> str:
|
|
425
|
+
"""Best-effort defense against common credential strings in diagnostics."""
|
|
426
|
+
|
|
427
|
+
if value is None:
|
|
428
|
+
return ""
|
|
429
|
+
redacted = str(value)
|
|
430
|
+
for secret in secrets:
|
|
431
|
+
if secret:
|
|
432
|
+
redacted = redacted.replace(secret, "<redacted>")
|
|
433
|
+
redacted = _BEARER_VALUE.sub("Bearer <redacted>", redacted)
|
|
434
|
+
redacted = _SENSITIVE_ASSIGNMENT.sub(
|
|
435
|
+
lambda match: f"{match.group(1)}{match.group(2)}<redacted>", redacted
|
|
436
|
+
)
|
|
437
|
+
return redacted
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def _optional_string(value: Any) -> str | None:
|
|
441
|
+
return value if isinstance(value, str) else None
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _format_request_context(method: str | None, path: str | None) -> str:
|
|
445
|
+
if method and path:
|
|
446
|
+
return f" ({method.upper()} {path})"
|
|
447
|
+
if method:
|
|
448
|
+
return f" ({method.upper()})"
|
|
449
|
+
if path:
|
|
450
|
+
return f" ({path})"
|
|
451
|
+
return ""
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
__all__ = [
|
|
455
|
+
"ApiError",
|
|
456
|
+
"AuthenticationError",
|
|
457
|
+
"BadRequestError",
|
|
458
|
+
"ConfigurationError",
|
|
459
|
+
"ConflictError",
|
|
460
|
+
"ForbiddenError",
|
|
461
|
+
"GoneError",
|
|
462
|
+
"MethodNotAllowedError",
|
|
463
|
+
"NotFoundError",
|
|
464
|
+
"OperationFailedError",
|
|
465
|
+
"OperationNeedsInputError",
|
|
466
|
+
"PollingError",
|
|
467
|
+
"PollTimeoutError",
|
|
468
|
+
"PreconditionFailedError",
|
|
469
|
+
"ProblemDetails",
|
|
470
|
+
"RateLimitError",
|
|
471
|
+
"RedBlackError",
|
|
472
|
+
"RequestTimeoutError",
|
|
473
|
+
"ResponseDecodeError",
|
|
474
|
+
"ServerError",
|
|
475
|
+
"TransportError",
|
|
476
|
+
"UnauthorizedError",
|
|
477
|
+
"UnprocessableContentError",
|
|
478
|
+
"api_error_from_response",
|
|
479
|
+
"extract_request_id",
|
|
480
|
+
"redact_text",
|
|
481
|
+
"safe_response_headers",
|
|
482
|
+
"sanitize_url",
|
|
483
|
+
]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Stable application-facing model aliases for RedBlack 2026.3.0.
|
|
2
|
+
|
|
3
|
+
Exact generated wire types remain available from
|
|
4
|
+
``redblack.models.generated``.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from . import generated as _generated
|
|
8
|
+
from .base import (
|
|
9
|
+
UNSET,
|
|
10
|
+
ODataPageModel,
|
|
11
|
+
OpenIntEnum,
|
|
12
|
+
RequestModel,
|
|
13
|
+
ResponseModel,
|
|
14
|
+
UnsetType,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
Account = _generated.AccountsAccountQueryRow
|
|
18
|
+
AccountExportJob = _generated.AccountsAccountExportJob
|
|
19
|
+
CashAction = _generated.CashActionCashActionQueryRow
|
|
20
|
+
CashActionCreate = _generated.CashActionsAddRequest
|
|
21
|
+
CashActionCreateResult = _generated.CashActionsAddResponse201
|
|
22
|
+
CashActionDeleteResult = _generated.CashActionsDeleteCashActionsResponse200
|
|
23
|
+
CashActionDetails = _generated.CashActionsGetResponse200
|
|
24
|
+
CashActionUpdate = _generated.CashActionsUpdateRequest
|
|
25
|
+
CashActionUpdateResult = _generated.CashActionsUpdateResponse200
|
|
26
|
+
Equivalent = _generated.EquivalentQueryEquivalentsResponse200ValueItem
|
|
27
|
+
EquivalentExportJob = _generated.EquivalentsEquivalentExportJob
|
|
28
|
+
Household = _generated.HouseholdsQueryHouseholdsResponse200ValueItem
|
|
29
|
+
HouseholdExportJob = _generated.HouseholdsHouseholdExportJob
|
|
30
|
+
Order = _generated.OrdersOrderQueryRow
|
|
31
|
+
OrderAddCancelResult = _generated.CommonResponseApiModelBase
|
|
32
|
+
OrderAddResult = _generated.ReviewSetsOrderMessageInOrderAddSessionQueryRow
|
|
33
|
+
OrderAddSession = _generated.ReviewSetsOrderAddSession
|
|
34
|
+
OrderAddSessionItem = _generated.ReviewSetsOrderInOrderAddSession
|
|
35
|
+
OrderCreate = _generated.ReviewSetsOrderAddRequest
|
|
36
|
+
OrderCreateResult = _generated.CommonEntityAddResult
|
|
37
|
+
OrderFilterType = _generated.OrdersOrderQueryFilterType
|
|
38
|
+
PendingOrder = _generated.ReviewSetsPendingOrderQueryRow
|
|
39
|
+
ProblemDetails = _generated.CommonProblemDetails
|
|
40
|
+
ReleaseError = _generated.ReviewSetsQueryReleaseResultsResponse200ValueItem
|
|
41
|
+
ReleaseValidationResult = _generated.ReviewSetsQueryReleaseValidationResultsResponse200ValueItem
|
|
42
|
+
Restriction = _generated.RestrictionQueryRestrictionsResponse200ValueItem
|
|
43
|
+
RestrictionExportJob = _generated.RestrictionsRestrictionExportJob
|
|
44
|
+
ReviewSet = _generated.ReviewSetsQueryResponse200ValueItem
|
|
45
|
+
ReviewSetReleaseSession = _generated.ReviewSetsGetReleaseSessionResponse200
|
|
46
|
+
Security = _generated.SecuritiesSecurityQueryRow
|
|
47
|
+
SweepSecurity = _generated.SecuritiesSweepSecurityQueryRow
|
|
48
|
+
Task = _generated.TasksGetResponse200
|
|
49
|
+
TaskCreate = _generated.TasksAddRequest
|
|
50
|
+
TaskCreateResult = _generated.TasksAddResponse201
|
|
51
|
+
TaskListItem = _generated.TasksTaskQueryRow
|
|
52
|
+
TaskStatus = _generated.TaskSettingsTaskStatusQueryRow
|
|
53
|
+
TaskType = _generated.TaskSettingsTaskTypeQueryRow
|
|
54
|
+
TaskUpdate = _generated.TasksTaskUpdateSpecification
|
|
55
|
+
TaskUpdateResult = _generated.TasksUpdateResponse200
|
|
56
|
+
User = _generated.UsersUserQueryRow
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"OpenIntEnum",
|
|
60
|
+
"ODataPageModel",
|
|
61
|
+
"RequestModel",
|
|
62
|
+
"ResponseModel",
|
|
63
|
+
"UNSET",
|
|
64
|
+
"UnsetType",
|
|
65
|
+
"Account",
|
|
66
|
+
"AccountExportJob",
|
|
67
|
+
"CashAction",
|
|
68
|
+
"CashActionCreate",
|
|
69
|
+
"CashActionCreateResult",
|
|
70
|
+
"CashActionDeleteResult",
|
|
71
|
+
"CashActionDetails",
|
|
72
|
+
"CashActionUpdate",
|
|
73
|
+
"CashActionUpdateResult",
|
|
74
|
+
"Equivalent",
|
|
75
|
+
"EquivalentExportJob",
|
|
76
|
+
"Household",
|
|
77
|
+
"HouseholdExportJob",
|
|
78
|
+
"Order",
|
|
79
|
+
"OrderAddCancelResult",
|
|
80
|
+
"OrderAddResult",
|
|
81
|
+
"OrderAddSession",
|
|
82
|
+
"OrderAddSessionItem",
|
|
83
|
+
"OrderCreate",
|
|
84
|
+
"OrderCreateResult",
|
|
85
|
+
"OrderFilterType",
|
|
86
|
+
"PendingOrder",
|
|
87
|
+
"ProblemDetails",
|
|
88
|
+
"ReleaseError",
|
|
89
|
+
"ReleaseValidationResult",
|
|
90
|
+
"Restriction",
|
|
91
|
+
"RestrictionExportJob",
|
|
92
|
+
"ReviewSet",
|
|
93
|
+
"ReviewSetReleaseSession",
|
|
94
|
+
"Security",
|
|
95
|
+
"SweepSecurity",
|
|
96
|
+
"Task",
|
|
97
|
+
"TaskCreate",
|
|
98
|
+
"TaskCreateResult",
|
|
99
|
+
"TaskListItem",
|
|
100
|
+
"TaskStatus",
|
|
101
|
+
"TaskType",
|
|
102
|
+
"TaskUpdate",
|
|
103
|
+
"TaskUpdateResult",
|
|
104
|
+
"User",
|
|
105
|
+
]
|