contextbase-base-client 0.5.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 (79) hide show
  1. base_client/.fern/metadata.json +13 -0
  2. base_client/CONTRIBUTING.md +125 -0
  3. base_client/README.md +176 -0
  4. base_client/__init__.py +159 -0
  5. base_client/_default_clients.py +30 -0
  6. base_client/bases/__init__.py +34 -0
  7. base_client/bases/client.py +256 -0
  8. base_client/bases/raw_client.py +365 -0
  9. base_client/bases/types/__init__.py +34 -0
  10. base_client/bases/types/list_bases_response.py +20 -0
  11. base_client/binding_plan/__init__.py +4 -0
  12. base_client/binding_plan/client.py +108 -0
  13. base_client/binding_plan/raw_client.py +128 -0
  14. base_client/bindings/__init__.py +34 -0
  15. base_client/bindings/client.py +242 -0
  16. base_client/bindings/raw_client.py +326 -0
  17. base_client/bindings/types/__init__.py +34 -0
  18. base_client/bindings/types/list_bindings_response.py +20 -0
  19. base_client/client.py +252 -0
  20. base_client/core/__init__.py +127 -0
  21. base_client/core/api_error.py +23 -0
  22. base_client/core/client_wrapper.py +102 -0
  23. base_client/core/datetime_utils.py +70 -0
  24. base_client/core/file.py +67 -0
  25. base_client/core/force_multipart.py +18 -0
  26. base_client/core/http_client.py +839 -0
  27. base_client/core/http_response.py +59 -0
  28. base_client/core/http_sse/__init__.py +42 -0
  29. base_client/core/http_sse/_api.py +170 -0
  30. base_client/core/http_sse/_decoders.py +61 -0
  31. base_client/core/http_sse/_exceptions.py +7 -0
  32. base_client/core/http_sse/_models.py +17 -0
  33. base_client/core/jsonable_encoder.py +120 -0
  34. base_client/core/logging.py +107 -0
  35. base_client/core/parse_error.py +36 -0
  36. base_client/core/pydantic_utilities.py +508 -0
  37. base_client/core/query_encoder.py +58 -0
  38. base_client/core/remove_none_from_dict.py +11 -0
  39. base_client/core/request_options.py +35 -0
  40. base_client/core/serialization.py +347 -0
  41. base_client/dsn/__init__.py +4 -0
  42. base_client/dsn/client.py +104 -0
  43. base_client/dsn/raw_client.py +128 -0
  44. base_client/errors/__init__.py +38 -0
  45. base_client/errors/bad_request_error.py +11 -0
  46. base_client/errors/forbidden_error.py +11 -0
  47. base_client/reference.md +591 -0
  48. base_client/sync_state/__init__.py +34 -0
  49. base_client/sync_state/client.py +206 -0
  50. base_client/sync_state/raw_client.py +280 -0
  51. base_client/sync_state/types/__init__.py +34 -0
  52. base_client/sync_state/types/sync_state_input_bindings_value.py +23 -0
  53. base_client/tests/conftest.py +21 -0
  54. base_client/tests/test_aiohttp_autodetect.py +113 -0
  55. base_client/types/__init__.py +126 -0
  56. base_client/types/api_key_auth.py +19 -0
  57. base_client/types/authenticated_account_ref.py +20 -0
  58. base_client/types/base.py +23 -0
  59. base_client/types/binding.py +29 -0
  60. base_client/types/binding_auth.py +72 -0
  61. base_client/types/binding_auth_input.py +77 -0
  62. base_client/types/binding_auth_input_api_key.py +19 -0
  63. base_client/types/binding_auth_input_authenticated_account.py +20 -0
  64. base_client/types/binding_auth_input_client_credentials.py +20 -0
  65. base_client/types/binding_auth_input_none.py +17 -0
  66. base_client/types/binding_auth_none.py +17 -0
  67. base_client/types/binding_models.py +23 -0
  68. base_client/types/binding_state.py +24 -0
  69. base_client/types/binding_state_response.py +21 -0
  70. base_client/types/client_credentials_auth.py +20 -0
  71. base_client/types/dagster_all_plan_binding.py +27 -0
  72. base_client/types/dagster_all_plan_binding_mode.py +5 -0
  73. base_client/types/dagster_binding_plan_all.py +23 -0
  74. base_client/types/dsn_response.py +19 -0
  75. base_client/types/error.py +19 -0
  76. base_client/types/run_status.py +10 -0
  77. contextbase_base_client-0.5.0.dist-info/METADATA +10 -0
  78. contextbase_base_client-0.5.0.dist-info/RECORD +79 -0
  79. contextbase_base_client-0.5.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,347 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import collections
4
+ import inspect
5
+ import typing
6
+
7
+ import pydantic
8
+ import typing_extensions
9
+
10
+
11
+ class FieldMetadata:
12
+ """
13
+ Metadata class used to annotate fields to provide additional information.
14
+
15
+ Example:
16
+ class MyDict(TypedDict):
17
+ field: typing.Annotated[str, FieldMetadata(alias="field_name")]
18
+
19
+ Will serialize: `{"field": "value"}`
20
+ To: `{"field_name": "value"}`
21
+ """
22
+
23
+ alias: str
24
+
25
+ def __init__(self, *, alias: str) -> None:
26
+ self.alias = alias
27
+
28
+
29
+ # Resolving type hints (typing.get_type_hints) is expensive because it eval/compiles
30
+ # forward-reference annotations. The result is constant for a given type, so we cache it.
31
+ # This is critical for hot paths like SSE event parsing, where the same (often large
32
+ # discriminated-union) type is converted on every single event.
33
+ _type_hints_cache: typing.Dict[typing.Any, typing.Dict[str, typing.Any]] = {}
34
+
35
+
36
+ def _get_cached_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
37
+ try:
38
+ cached = _type_hints_cache.get(expected_type)
39
+ except TypeError:
40
+ # Unhashable type; resolve without caching.
41
+ return _resolve_type_hints(expected_type)
42
+ if cached is None:
43
+ cached = _resolve_type_hints(expected_type)
44
+ _type_hints_cache[expected_type] = cached
45
+ return cached
46
+
47
+
48
+ def _resolve_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]:
49
+ try:
50
+ return typing_extensions.get_type_hints(expected_type, include_extras=True)
51
+ except NameError:
52
+ # The type contains a circular reference, so we use the __annotations__ attribute directly.
53
+ return getattr(expected_type, "__annotations__", {})
54
+
55
+
56
+ # Whether convert_and_respect_annotation_metadata can possibly rewrite anything for a given
57
+ # annotation, i.e. whether any reachable model/TypedDict field carries a FieldMetadata alias.
58
+ # This is constant per type, so we cache it and use it to short-circuit the recursive walk.
59
+ _requires_conversion_cache: typing.Dict[typing.Any, bool] = {}
60
+
61
+
62
+ def _requires_conversion(type_: typing.Any) -> bool:
63
+ try:
64
+ cached = _requires_conversion_cache.get(type_)
65
+ except TypeError:
66
+ # Unhashable annotation; compute without caching.
67
+ return _compute_requires_conversion(type_, set())
68
+ if cached is None:
69
+ cached = _compute_requires_conversion(type_, set())
70
+ _requires_conversion_cache[type_] = cached
71
+ return cached
72
+
73
+
74
+ def _compute_requires_conversion(type_: typing.Any, seen: typing.Set[typing.Any]) -> bool:
75
+ clean_type = _remove_annotations(type_)
76
+
77
+ try:
78
+ if clean_type in seen:
79
+ return False
80
+ seen = seen | {clean_type}
81
+ except TypeError:
82
+ # Unhashable type; skip cycle tracking (the type graph is finite in practice).
83
+ pass
84
+
85
+ # Models / TypedDicts: a field alias here means we must dealias; otherwise recurse into fields.
86
+ if (inspect.isclass(clean_type) and issubclass(clean_type, pydantic.BaseModel)) or typing_extensions.is_typeddict(
87
+ clean_type
88
+ ):
89
+ annotations = _get_cached_type_hints(clean_type)
90
+ if _get_alias_to_field_name(annotations):
91
+ return True
92
+ return any(_compute_requires_conversion(hint, seen) for hint in annotations.values())
93
+
94
+ # Containers / unions: recurse into the type arguments (List/Set/Sequence/Dict/Union/etc.).
95
+ return any(_compute_requires_conversion(arg, seen) for arg in typing_extensions.get_args(clean_type))
96
+
97
+
98
+ def convert_and_respect_annotation_metadata(
99
+ *,
100
+ object_: typing.Any,
101
+ annotation: typing.Any,
102
+ inner_type: typing.Optional[typing.Any] = None,
103
+ direction: typing.Literal["read", "write"],
104
+ ) -> typing.Any:
105
+ """
106
+ Respect the metadata annotations on a field, such as aliasing. This function effectively
107
+ manipulates the dict-form of an object to respect the metadata annotations. This is primarily used for
108
+ TypedDicts, which cannot support aliasing out of the box, and can be extended for additional
109
+ utilities, such as defaults.
110
+
111
+ Parameters
112
+ ----------
113
+ object_ : typing.Any
114
+
115
+ annotation : type
116
+ The type we're looking to apply typing annotations from
117
+
118
+ inner_type : typing.Optional[type]
119
+
120
+ Returns
121
+ -------
122
+ typing.Any
123
+ """
124
+
125
+ if object_ is None:
126
+ return None
127
+ if inner_type is None:
128
+ inner_type = annotation
129
+ # The only thing this function ever rewrites is keys that carry a FieldMetadata
130
+ # alias. If nothing in the (cached) type graph has such an alias, the conversion is
131
+ # a content-identity transform, so we can skip the entire recursive walk. This is
132
+ # the hot path for SSE streaming, where a large discriminated union would otherwise
133
+ # be traversed on every single event.
134
+ if not _requires_conversion(annotation):
135
+ return object_
136
+
137
+ clean_type = _remove_annotations(inner_type)
138
+ # Pydantic models
139
+ if (
140
+ inspect.isclass(clean_type)
141
+ and issubclass(clean_type, pydantic.BaseModel)
142
+ and isinstance(object_, typing.Mapping)
143
+ ):
144
+ return _convert_mapping(object_, clean_type, direction)
145
+ # TypedDicts
146
+ if typing_extensions.is_typeddict(clean_type) and isinstance(object_, typing.Mapping):
147
+ return _convert_mapping(object_, clean_type, direction)
148
+
149
+ if (
150
+ typing_extensions.get_origin(clean_type) == typing.Dict
151
+ or typing_extensions.get_origin(clean_type) == dict
152
+ or clean_type == typing.Dict
153
+ ) and isinstance(object_, typing.Dict):
154
+ key_type = typing_extensions.get_args(clean_type)[0]
155
+ value_type = typing_extensions.get_args(clean_type)[1]
156
+
157
+ return {
158
+ key: convert_and_respect_annotation_metadata(
159
+ object_=value,
160
+ annotation=annotation,
161
+ inner_type=value_type,
162
+ direction=direction,
163
+ )
164
+ for key, value in object_.items()
165
+ }
166
+
167
+ # If you're iterating on a string, do not bother to coerce it to a sequence.
168
+ if not isinstance(object_, str):
169
+ if (
170
+ typing_extensions.get_origin(clean_type) == typing.Set
171
+ or typing_extensions.get_origin(clean_type) == set
172
+ or clean_type == typing.Set
173
+ ) and isinstance(object_, typing.Set):
174
+ inner_type = typing_extensions.get_args(clean_type)[0]
175
+ return {
176
+ convert_and_respect_annotation_metadata(
177
+ object_=item,
178
+ annotation=annotation,
179
+ inner_type=inner_type,
180
+ direction=direction,
181
+ )
182
+ for item in object_
183
+ }
184
+ elif (
185
+ (
186
+ typing_extensions.get_origin(clean_type) == typing.List
187
+ or typing_extensions.get_origin(clean_type) == list
188
+ or clean_type == typing.List
189
+ )
190
+ and isinstance(object_, typing.List)
191
+ ) or (
192
+ (
193
+ typing_extensions.get_origin(clean_type) == typing.Sequence
194
+ or typing_extensions.get_origin(clean_type) == collections.abc.Sequence
195
+ or clean_type == typing.Sequence
196
+ )
197
+ and isinstance(object_, typing.Sequence)
198
+ ):
199
+ inner_type = typing_extensions.get_args(clean_type)[0]
200
+ return [
201
+ convert_and_respect_annotation_metadata(
202
+ object_=item,
203
+ annotation=annotation,
204
+ inner_type=inner_type,
205
+ direction=direction,
206
+ )
207
+ for item in object_
208
+ ]
209
+
210
+ if typing_extensions.get_origin(clean_type) == typing.Union:
211
+ # We should be able to ~relatively~ safely try to convert keys against all
212
+ # member types in the union, the edge case here is if one member aliases a field
213
+ # of the same name to a different name from another member
214
+ # Or if another member aliases a field of the same name that another member does not.
215
+ for member in typing_extensions.get_args(clean_type):
216
+ object_ = convert_and_respect_annotation_metadata(
217
+ object_=object_,
218
+ annotation=annotation,
219
+ inner_type=member,
220
+ direction=direction,
221
+ )
222
+ return object_
223
+
224
+ annotated_type = _get_annotation(annotation)
225
+ if annotated_type is None:
226
+ return object_
227
+
228
+ # If the object is not a TypedDict, a Union, or other container (list, set, sequence, etc.)
229
+ # Then we can safely call it on the recursive conversion.
230
+ return object_
231
+
232
+
233
+ def _convert_mapping(
234
+ object_: typing.Mapping[str, object],
235
+ expected_type: typing.Any,
236
+ direction: typing.Literal["read", "write"],
237
+ ) -> typing.Mapping[str, object]:
238
+ converted_object: typing.Dict[str, object] = {}
239
+ annotations = _get_cached_type_hints(expected_type)
240
+ aliases_to_field_names = _get_alias_to_field_name(annotations)
241
+ for key, value in object_.items():
242
+ if direction == "read" and key in aliases_to_field_names:
243
+ dealiased_key = aliases_to_field_names.get(key)
244
+ if dealiased_key is not None:
245
+ type_ = annotations.get(dealiased_key)
246
+ else:
247
+ type_ = annotations.get(key)
248
+ # Note you can't get the annotation by the field name if you're in read mode, so you must check the aliases map
249
+ #
250
+ # So this is effectively saying if we're in write mode, and we don't have a type, or if we're in read mode and we don't have an alias
251
+ # then we can just pass the value through as is
252
+ if type_ is None:
253
+ converted_object[key] = value
254
+ elif direction == "read" and key not in aliases_to_field_names:
255
+ converted_object[key] = convert_and_respect_annotation_metadata(
256
+ object_=value, annotation=type_, direction=direction
257
+ )
258
+ else:
259
+ converted_object[_alias_key(key, type_, direction, aliases_to_field_names)] = (
260
+ convert_and_respect_annotation_metadata(object_=value, annotation=type_, direction=direction)
261
+ )
262
+ return converted_object
263
+
264
+
265
+ def _get_annotation(type_: typing.Any) -> typing.Optional[typing.Any]:
266
+ maybe_annotated_type = typing_extensions.get_origin(type_)
267
+ if maybe_annotated_type is None:
268
+ return None
269
+
270
+ if maybe_annotated_type == typing_extensions.NotRequired:
271
+ type_ = typing_extensions.get_args(type_)[0]
272
+ maybe_annotated_type = typing_extensions.get_origin(type_)
273
+
274
+ if maybe_annotated_type == typing_extensions.Annotated:
275
+ return type_
276
+
277
+ return None
278
+
279
+
280
+ def _remove_annotations(type_: typing.Any) -> typing.Any:
281
+ maybe_annotated_type = typing_extensions.get_origin(type_)
282
+ if maybe_annotated_type is None:
283
+ return type_
284
+
285
+ if maybe_annotated_type == typing_extensions.NotRequired:
286
+ return _remove_annotations(typing_extensions.get_args(type_)[0])
287
+
288
+ if maybe_annotated_type == typing_extensions.Annotated:
289
+ return _remove_annotations(typing_extensions.get_args(type_)[0])
290
+
291
+ return type_
292
+
293
+
294
+ def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]:
295
+ annotations = _get_cached_type_hints(type_)
296
+ return _get_alias_to_field_name(annotations)
297
+
298
+
299
+ def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]:
300
+ annotations = _get_cached_type_hints(type_)
301
+ return _get_field_to_alias_name(annotations)
302
+
303
+
304
+ def _get_alias_to_field_name(
305
+ field_to_hint: typing.Dict[str, typing.Any],
306
+ ) -> typing.Dict[str, str]:
307
+ aliases = {}
308
+ for field, hint in field_to_hint.items():
309
+ maybe_alias = _get_alias_from_type(hint)
310
+ if maybe_alias is not None:
311
+ aliases[maybe_alias] = field
312
+ return aliases
313
+
314
+
315
+ def _get_field_to_alias_name(
316
+ field_to_hint: typing.Dict[str, typing.Any],
317
+ ) -> typing.Dict[str, str]:
318
+ aliases = {}
319
+ for field, hint in field_to_hint.items():
320
+ maybe_alias = _get_alias_from_type(hint)
321
+ if maybe_alias is not None:
322
+ aliases[field] = maybe_alias
323
+ return aliases
324
+
325
+
326
+ def _get_alias_from_type(type_: typing.Any) -> typing.Optional[str]:
327
+ maybe_annotated_type = _get_annotation(type_)
328
+
329
+ if maybe_annotated_type is not None:
330
+ # The actual annotations are 1 onward, the first is the annotated type
331
+ annotations = typing_extensions.get_args(maybe_annotated_type)[1:]
332
+
333
+ for annotation in annotations:
334
+ if isinstance(annotation, FieldMetadata) and annotation.alias is not None:
335
+ return annotation.alias
336
+ return None
337
+
338
+
339
+ def _alias_key(
340
+ key: str,
341
+ type_: typing.Any,
342
+ direction: typing.Literal["read", "write"],
343
+ aliases_to_field_names: typing.Dict[str, str],
344
+ ) -> str:
345
+ if direction == "read":
346
+ return aliases_to_field_names.get(key, key)
347
+ return _get_alias_from_type(type_=type_) or key
@@ -0,0 +1,4 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ # isort: skip_file
4
+
@@ -0,0 +1,104 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
6
+ from ..core.request_options import RequestOptions
7
+ from ..types.dsn_response import DsnResponse
8
+ from .raw_client import AsyncRawDsnClient, RawDsnClient
9
+
10
+
11
+ class DsnClient:
12
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
13
+ self._raw_client = RawDsnClient(client_wrapper=client_wrapper)
14
+
15
+ @property
16
+ def with_raw_response(self) -> RawDsnClient:
17
+ """
18
+ Retrieves a raw implementation of this client that returns raw responses.
19
+
20
+ Returns
21
+ -------
22
+ RawDsnClient
23
+ """
24
+ return self._raw_client
25
+
26
+ def get_dsn(self, base_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> DsnResponse:
27
+ """
28
+ Parameters
29
+ ----------
30
+ base_id : str
31
+
32
+ request_options : typing.Optional[RequestOptions]
33
+ Request-specific configuration.
34
+
35
+ Returns
36
+ -------
37
+ DsnResponse
38
+ The base's resolved DSN.
39
+
40
+ Examples
41
+ --------
42
+ from base_client import BaseAPIClient
43
+
44
+ client = BaseAPIClient(
45
+ base_url="https://yourhost.com/path/to/api",
46
+ )
47
+ client.dsn.get_dsn(
48
+ base_id="base_id",
49
+ )
50
+ """
51
+ _response = self._raw_client.get_dsn(base_id, request_options=request_options)
52
+ return _response.data
53
+
54
+
55
+ class AsyncDsnClient:
56
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
57
+ self._raw_client = AsyncRawDsnClient(client_wrapper=client_wrapper)
58
+
59
+ @property
60
+ def with_raw_response(self) -> AsyncRawDsnClient:
61
+ """
62
+ Retrieves a raw implementation of this client that returns raw responses.
63
+
64
+ Returns
65
+ -------
66
+ AsyncRawDsnClient
67
+ """
68
+ return self._raw_client
69
+
70
+ async def get_dsn(self, base_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> DsnResponse:
71
+ """
72
+ Parameters
73
+ ----------
74
+ base_id : str
75
+
76
+ request_options : typing.Optional[RequestOptions]
77
+ Request-specific configuration.
78
+
79
+ Returns
80
+ -------
81
+ DsnResponse
82
+ The base's resolved DSN.
83
+
84
+ Examples
85
+ --------
86
+ import asyncio
87
+
88
+ from base_client import AsyncBaseAPIClient
89
+
90
+ client = AsyncBaseAPIClient(
91
+ base_url="https://yourhost.com/path/to/api",
92
+ )
93
+
94
+
95
+ async def main() -> None:
96
+ await client.dsn.get_dsn(
97
+ base_id="base_id",
98
+ )
99
+
100
+
101
+ asyncio.run(main())
102
+ """
103
+ _response = await self._raw_client.get_dsn(base_id, request_options=request_options)
104
+ return _response.data
@@ -0,0 +1,128 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+ from json.decoder import JSONDecodeError
5
+
6
+ from ..core.api_error import ApiError
7
+ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
8
+ from ..core.http_response import AsyncHttpResponse, HttpResponse
9
+ from ..core.jsonable_encoder import encode_path_param
10
+ from ..core.parse_error import ParsingError
11
+ from ..core.pydantic_utilities import parse_obj_as
12
+ from ..core.request_options import RequestOptions
13
+ from ..errors.forbidden_error import ForbiddenError
14
+ from ..types.dsn_response import DsnResponse
15
+ from ..types.error import Error
16
+ from pydantic import ValidationError
17
+
18
+
19
+ class RawDsnClient:
20
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
21
+ self._client_wrapper = client_wrapper
22
+
23
+ def get_dsn(
24
+ self, base_id: str, *, request_options: typing.Optional[RequestOptions] = None
25
+ ) -> HttpResponse[DsnResponse]:
26
+ """
27
+ Parameters
28
+ ----------
29
+ base_id : str
30
+
31
+ request_options : typing.Optional[RequestOptions]
32
+ Request-specific configuration.
33
+
34
+ Returns
35
+ -------
36
+ HttpResponse[DsnResponse]
37
+ The base's resolved DSN.
38
+ """
39
+ _response = self._client_wrapper.httpx_client.request(
40
+ f"base/bases/{encode_path_param(base_id)}/dsn",
41
+ method="GET",
42
+ request_options=request_options,
43
+ )
44
+ try:
45
+ if 200 <= _response.status_code < 300:
46
+ _data = typing.cast(
47
+ DsnResponse,
48
+ parse_obj_as(
49
+ type_=DsnResponse, # type: ignore
50
+ object_=_response.json(),
51
+ ),
52
+ )
53
+ return HttpResponse(response=_response, data=_data)
54
+ if _response.status_code == 403:
55
+ raise ForbiddenError(
56
+ headers=dict(_response.headers),
57
+ body=typing.cast(
58
+ Error,
59
+ parse_obj_as(
60
+ type_=Error, # type: ignore
61
+ object_=_response.json(),
62
+ ),
63
+ ),
64
+ )
65
+ _response_json = _response.json()
66
+ except JSONDecodeError:
67
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
68
+ except ValidationError as e:
69
+ raise ParsingError(
70
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
71
+ )
72
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
73
+
74
+
75
+ class AsyncRawDsnClient:
76
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
77
+ self._client_wrapper = client_wrapper
78
+
79
+ async def get_dsn(
80
+ self, base_id: str, *, request_options: typing.Optional[RequestOptions] = None
81
+ ) -> AsyncHttpResponse[DsnResponse]:
82
+ """
83
+ Parameters
84
+ ----------
85
+ base_id : str
86
+
87
+ request_options : typing.Optional[RequestOptions]
88
+ Request-specific configuration.
89
+
90
+ Returns
91
+ -------
92
+ AsyncHttpResponse[DsnResponse]
93
+ The base's resolved DSN.
94
+ """
95
+ _response = await self._client_wrapper.httpx_client.request(
96
+ f"base/bases/{encode_path_param(base_id)}/dsn",
97
+ method="GET",
98
+ request_options=request_options,
99
+ )
100
+ try:
101
+ if 200 <= _response.status_code < 300:
102
+ _data = typing.cast(
103
+ DsnResponse,
104
+ parse_obj_as(
105
+ type_=DsnResponse, # type: ignore
106
+ object_=_response.json(),
107
+ ),
108
+ )
109
+ return AsyncHttpResponse(response=_response, data=_data)
110
+ if _response.status_code == 403:
111
+ raise ForbiddenError(
112
+ headers=dict(_response.headers),
113
+ body=typing.cast(
114
+ Error,
115
+ parse_obj_as(
116
+ type_=Error, # type: ignore
117
+ object_=_response.json(),
118
+ ),
119
+ ),
120
+ )
121
+ _response_json = _response.json()
122
+ except JSONDecodeError:
123
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
124
+ except ValidationError as e:
125
+ raise ParsingError(
126
+ status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e
127
+ )
128
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
@@ -0,0 +1,38 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ # isort: skip_file
4
+
5
+ import typing
6
+ from importlib import import_module
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from .bad_request_error import BadRequestError
10
+ from .forbidden_error import ForbiddenError
11
+ _dynamic_imports: typing.Dict[str, str] = {
12
+ "BadRequestError": ".bad_request_error",
13
+ "ForbiddenError": ".forbidden_error",
14
+ }
15
+
16
+
17
+ def __getattr__(attr_name: str) -> typing.Any:
18
+ module_name = _dynamic_imports.get(attr_name)
19
+ if module_name is None:
20
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
21
+ try:
22
+ module = import_module(module_name, __package__)
23
+ if module_name == f".{attr_name}":
24
+ return module
25
+ else:
26
+ return getattr(module, attr_name)
27
+ except ImportError as e:
28
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
29
+ except AttributeError as e:
30
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
31
+
32
+
33
+ def __dir__():
34
+ lazy_attrs = list(_dynamic_imports.keys())
35
+ return sorted(lazy_attrs)
36
+
37
+
38
+ __all__ = ["BadRequestError", "ForbiddenError"]
@@ -0,0 +1,11 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.api_error import ApiError
6
+ from ..types.error import Error
7
+
8
+
9
+ class BadRequestError(ApiError):
10
+ def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None):
11
+ super().__init__(status_code=400, headers=headers, body=body)
@@ -0,0 +1,11 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ from ..core.api_error import ApiError
6
+ from ..types.error import Error
7
+
8
+
9
+ class ForbiddenError(ApiError):
10
+ def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None):
11
+ super().__init__(status_code=403, headers=headers, body=body)