levelfour 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 (208) hide show
  1. levelfour/__init__.py +495 -0
  2. levelfour/api_keys/__init__.py +34 -0
  3. levelfour/api_keys/client.py +337 -0
  4. levelfour/api_keys/raw_client.py +621 -0
  5. levelfour/api_keys/types/__init__.py +34 -0
  6. levelfour/api_keys/types/create_api_key_request_scope.py +5 -0
  7. levelfour/async_levelfour_client.py +39 -0
  8. levelfour/base_client.py +346 -0
  9. levelfour/client.py +54 -0
  10. levelfour/core/__init__.py +127 -0
  11. levelfour/core/api_error.py +23 -0
  12. levelfour/core/client_wrapper.py +105 -0
  13. levelfour/core/datetime_utils.py +70 -0
  14. levelfour/core/file.py +67 -0
  15. levelfour/core/force_multipart.py +18 -0
  16. levelfour/core/http_client.py +776 -0
  17. levelfour/core/http_response.py +59 -0
  18. levelfour/core/http_sse/__init__.py +42 -0
  19. levelfour/core/http_sse/_api.py +112 -0
  20. levelfour/core/http_sse/_decoders.py +61 -0
  21. levelfour/core/http_sse/_exceptions.py +7 -0
  22. levelfour/core/http_sse/_models.py +17 -0
  23. levelfour/core/jsonable_encoder.py +108 -0
  24. levelfour/core/logging.py +107 -0
  25. levelfour/core/pagination.py +82 -0
  26. levelfour/core/pydantic_utilities.py +577 -0
  27. levelfour/core/query_encoder.py +58 -0
  28. levelfour/core/remove_none_from_dict.py +11 -0
  29. levelfour/core/request_options.py +35 -0
  30. levelfour/core/serialization.py +276 -0
  31. levelfour/errors/__init__.py +40 -0
  32. levelfour/errors/forbidden_error.py +11 -0
  33. levelfour/errors/unauthorized_error.py +11 -0
  34. levelfour/errors/unprocessable_entity_error.py +11 -0
  35. levelfour/evaluation/__init__.py +4 -0
  36. levelfour/evaluation/client.py +102 -0
  37. levelfour/evaluation/raw_client.py +141 -0
  38. levelfour/exceptions.py +63 -0
  39. levelfour/health/__init__.py +4 -0
  40. levelfour/health/client.py +163 -0
  41. levelfour/health/raw_client.py +164 -0
  42. levelfour/integrations/__init__.py +0 -0
  43. levelfour/integrations/django.py +26 -0
  44. levelfour/integrations/fastapi.py +39 -0
  45. levelfour/integrations/telemetry.py +66 -0
  46. levelfour/levelfour_client.py +47 -0
  47. levelfour/pagination.py +84 -0
  48. levelfour/pr_analysis/__init__.py +4 -0
  49. levelfour/pr_analysis/client.py +408 -0
  50. levelfour/pr_analysis/raw_client.py +620 -0
  51. levelfour/pr_analysis_actions/__init__.py +4 -0
  52. levelfour/pr_analysis_actions/client.py +588 -0
  53. levelfour/pr_analysis_actions/raw_client.py +939 -0
  54. levelfour/pr_analysis_installations/__init__.py +4 -0
  55. levelfour/pr_analysis_installations/client.py +470 -0
  56. levelfour/pr_analysis_installations/raw_client.py +868 -0
  57. levelfour/providers/__init__.py +34 -0
  58. levelfour/providers/client.py +1504 -0
  59. levelfour/providers/raw_client.py +2398 -0
  60. levelfour/providers/types/__init__.py +36 -0
  61. levelfour/providers/types/list_spending_breakdown_providers_response.py +11 -0
  62. levelfour/py.typed +0 -0
  63. levelfour/recommendations/__init__.py +4 -0
  64. levelfour/recommendations/client.py +1018 -0
  65. levelfour/recommendations/raw_client.py +1810 -0
  66. levelfour/savings/__init__.py +4 -0
  67. levelfour/savings/client.py +401 -0
  68. levelfour/savings/raw_client.py +624 -0
  69. levelfour/spending/__init__.py +34 -0
  70. levelfour/spending/client.py +437 -0
  71. levelfour/spending/raw_client.py +684 -0
  72. levelfour/spending/types/__init__.py +34 -0
  73. levelfour/spending/types/list_breakdown_spending_response.py +12 -0
  74. levelfour/types/__init__.py +413 -0
  75. levelfour/types/accept_terms_data.py +25 -0
  76. levelfour/types/accept_terms_response.py +27 -0
  77. levelfour/types/action_request.py +42 -0
  78. levelfour/types/api_key_created_data.py +28 -0
  79. levelfour/types/api_key_data.py +27 -0
  80. levelfour/types/authentication_error.py +5 -0
  81. levelfour/types/authorization_error.py +5 -0
  82. levelfour/types/bad_request_error.py +5 -0
  83. levelfour/types/comparison_item.py +32 -0
  84. levelfour/types/complete_wizard_data.py +27 -0
  85. levelfour/types/complete_wizard_response.py +27 -0
  86. levelfour/types/conflict_error.py +5 -0
  87. levelfour/types/cost_breakdown_data.py +35 -0
  88. levelfour/types/cost_breakdown_item.py +71 -0
  89. levelfour/types/cost_breakdown_raw_data.py +37 -0
  90. levelfour/types/cost_breakdown_raw_response.py +27 -0
  91. levelfour/types/cost_breakdown_response.py +27 -0
  92. levelfour/types/costs_breakdown_chart_data.py +48 -0
  93. levelfour/types/costs_breakdown_chart_response.py +27 -0
  94. levelfour/types/costs_breakdown_data_point.py +46 -0
  95. levelfour/types/costs_breakdown_grouped_chart_data.py +52 -0
  96. levelfour/types/costs_breakdown_grouped_chart_response.py +27 -0
  97. levelfour/types/costs_breakdown_grouped_data_point.py +36 -0
  98. levelfour/types/costs_breakdown_totals.py +41 -0
  99. levelfour/types/create_api_key_response.py +23 -0
  100. levelfour/types/daily_spending_breakdown_data.py +42 -0
  101. levelfour/types/daily_spending_breakdown_response.py +27 -0
  102. levelfour/types/daily_spending_data_point.py +31 -0
  103. levelfour/types/dashboard_summary_data.py +72 -0
  104. levelfour/types/dashboard_summary_response.py +27 -0
  105. levelfour/types/delete_endpoint_data.py +20 -0
  106. levelfour/types/delete_endpoint_response.py +23 -0
  107. levelfour/types/endpoint_data.py +23 -0
  108. levelfour/types/error_response.py +23 -0
  109. levelfour/types/error_response_error.py +21 -0
  110. levelfour/types/evaluation_status_data.py +71 -0
  111. levelfour/types/evaluation_status_response.py +27 -0
  112. levelfour/types/execution_log_command.py +21 -0
  113. levelfour/types/execution_log_entry.py +23 -0
  114. levelfour/types/execution_step_schema.py +23 -0
  115. levelfour/types/http_validation_error.py +20 -0
  116. levelfour/types/implementation_method_data.py +25 -0
  117. levelfour/types/implementation_method_response.py +27 -0
  118. levelfour/types/implementation_step.py +27 -0
  119. levelfour/types/internal_server_error.py +5 -0
  120. levelfour/types/list_api_keys_data.py +21 -0
  121. levelfour/types/list_api_keys_response.py +23 -0
  122. levelfour/types/list_endpoints_response.py +23 -0
  123. levelfour/types/metric_data_point.py +27 -0
  124. levelfour/types/metric_series.py +40 -0
  125. levelfour/types/metric_series_entry.py +25 -0
  126. levelfour/types/monthly_spending_data.py +27 -0
  127. levelfour/types/monthly_spending_data_point.py +31 -0
  128. levelfour/types/monthly_spending_response.py +27 -0
  129. levelfour/types/not_found_error.py +5 -0
  130. levelfour/types/pagination_meta.py +51 -0
  131. levelfour/types/processing_recommendation_item.py +61 -0
  132. levelfour/types/processing_recommendations_data.py +28 -0
  133. levelfour/types/processing_recommendations_response.py +27 -0
  134. levelfour/types/provider.py +46 -0
  135. levelfour/types/provider_breakdown_data.py +31 -0
  136. levelfour/types/provider_breakdown_item.py +77 -0
  137. levelfour/types/provider_breakdown_response.py +27 -0
  138. levelfour/types/provider_filter_options_data.py +51 -0
  139. levelfour/types/provider_filter_options_response.py +27 -0
  140. levelfour/types/provider_potential_savings_summary_data.py +46 -0
  141. levelfour/types/provider_potential_savings_summary_response.py +27 -0
  142. levelfour/types/provider_recommendations_summary_data.py +37 -0
  143. levelfour/types/provider_recommendations_summary_response.py +27 -0
  144. levelfour/types/provider_saved_summary_data.py +56 -0
  145. levelfour/types/provider_saved_summary_response.py +27 -0
  146. levelfour/types/provider_service_breakdown_data.py +65 -0
  147. levelfour/types/provider_service_breakdown_item.py +72 -0
  148. levelfour/types/provider_service_breakdown_raw_data.py +62 -0
  149. levelfour/types/provider_service_breakdown_raw_response.py +27 -0
  150. levelfour/types/provider_service_breakdown_response.py +27 -0
  151. levelfour/types/provider_service_costs_breakdown_data.py +62 -0
  152. levelfour/types/provider_service_costs_breakdown_response.py +27 -0
  153. levelfour/types/provider_service_costs_data_point.py +36 -0
  154. levelfour/types/provider_spending.py +61 -0
  155. levelfour/types/provider_spending_summary_data.py +67 -0
  156. levelfour/types/provider_spending_summary_response.py +27 -0
  157. levelfour/types/provider_timeline_data.py +57 -0
  158. levelfour/types/provider_timeline_response.py +27 -0
  159. levelfour/types/provider_top_service.py +36 -0
  160. levelfour/types/providers_summary_data.py +24 -0
  161. levelfour/types/providers_summary_response.py +27 -0
  162. levelfour/types/rate_limit_error.py +5 -0
  163. levelfour/types/recommendation_actions.py +60 -0
  164. levelfour/types/recommendation_detail.py +42 -0
  165. levelfour/types/recommendation_detail_response.py +27 -0
  166. levelfour/types/recommendation_filter_options_data.py +41 -0
  167. levelfour/types/recommendation_filter_options_response.py +27 -0
  168. levelfour/types/recommendation_item.py +36 -0
  169. levelfour/types/recommendation_wizard_data.py +173 -0
  170. levelfour/types/recommendation_wizard_data_metrics.py +7 -0
  171. levelfour/types/recommendation_wizard_response.py +27 -0
  172. levelfour/types/recommendations_breakdown_data.py +26 -0
  173. levelfour/types/recommendations_breakdown_response.py +27 -0
  174. levelfour/types/recommendations_overview_data.py +51 -0
  175. levelfour/types/recommendations_overview_response.py +27 -0
  176. levelfour/types/register_endpoint_response.py +23 -0
  177. levelfour/types/resource_change.py +42 -0
  178. levelfour/types/resource_snapshot.py +21 -0
  179. levelfour/types/revoke_api_key_data.py +20 -0
  180. levelfour/types/revoke_api_key_response.py +23 -0
  181. levelfour/types/risk_assessment.py +29 -0
  182. levelfour/types/risk_factor.py +27 -0
  183. levelfour/types/saved_breakdown_data.py +31 -0
  184. levelfour/types/saved_breakdown_response.py +27 -0
  185. levelfour/types/saved_by_provider_breakdown_data.py +31 -0
  186. levelfour/types/saved_by_provider_breakdown_response.py +27 -0
  187. levelfour/types/saved_by_provider_item.py +76 -0
  188. levelfour/types/saved_item.py +35 -0
  189. levelfour/types/saved_provider_summary.py +36 -0
  190. levelfour/types/saved_summary_data.py +57 -0
  191. levelfour/types/saved_summary_response.py +27 -0
  192. levelfour/types/savings_decision_data.py +28 -0
  193. levelfour/types/savings_decision_response.py +27 -0
  194. levelfour/types/spending_by_date_item.py +36 -0
  195. levelfour/types/top_recommendation.py +56 -0
  196. levelfour/types/trigger_info.py +22 -0
  197. levelfour/types/update_tag_data.py +20 -0
  198. levelfour/types/update_tag_response.py +23 -0
  199. levelfour/types/validation_error.py +24 -0
  200. levelfour/types/validation_error_loc_item.py +5 -0
  201. levelfour/webhooks/__init__.py +4 -0
  202. levelfour/webhooks/client.py +273 -0
  203. levelfour/webhooks/raw_client.py +483 -0
  204. levelfour/webhooks/verifier.py +61 -0
  205. levelfour-0.1.0.dist-info/METADATA +12 -0
  206. levelfour-0.1.0.dist-info/RECORD +208 -0
  207. levelfour-0.1.0.dist-info/WHEEL +5 -0
  208. levelfour-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,621 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import datetime as dt
4
+ import typing
5
+ from json.decoder import JSONDecodeError
6
+
7
+ from ..core.api_error import ApiError
8
+ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper
9
+ from ..core.http_response import AsyncHttpResponse, HttpResponse
10
+ from ..core.jsonable_encoder import jsonable_encoder
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 ..errors.unauthorized_error import UnauthorizedError
15
+ from ..errors.unprocessable_entity_error import UnprocessableEntityError
16
+ from ..types.authentication_error import AuthenticationError
17
+ from ..types.authorization_error import AuthorizationError
18
+ from ..types.create_api_key_response import CreateApiKeyResponse
19
+ from ..types.http_validation_error import HttpValidationError
20
+ from ..types.list_api_keys_response import ListApiKeysResponse
21
+ from ..types.revoke_api_key_response import RevokeApiKeyResponse
22
+ from .types.create_api_key_request_scope import CreateApiKeyRequestScope
23
+
24
+ # this is used as the default value for optional parameters
25
+ OMIT = typing.cast(typing.Any, ...)
26
+
27
+
28
+ class RawApiKeysClient:
29
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
30
+ self._client_wrapper = client_wrapper
31
+
32
+ def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[ListApiKeysResponse]:
33
+ """
34
+ Parameters
35
+ ----------
36
+ request_options : typing.Optional[RequestOptions]
37
+ Request-specific configuration.
38
+
39
+ Returns
40
+ -------
41
+ HttpResponse[ListApiKeysResponse]
42
+ Successful Response
43
+ """
44
+ _response = self._client_wrapper.httpx_client.request(
45
+ "api/v1/api-keys",
46
+ method="GET",
47
+ request_options=request_options,
48
+ )
49
+ try:
50
+ if 200 <= _response.status_code < 300:
51
+ _data = typing.cast(
52
+ ListApiKeysResponse,
53
+ parse_obj_as(
54
+ type_=ListApiKeysResponse, # type: ignore
55
+ object_=_response.json(),
56
+ ),
57
+ )
58
+ return HttpResponse(response=_response, data=_data)
59
+ if _response.status_code == 401:
60
+ raise UnauthorizedError(
61
+ headers=dict(_response.headers),
62
+ body=typing.cast(
63
+ AuthenticationError,
64
+ parse_obj_as(
65
+ type_=AuthenticationError, # type: ignore
66
+ object_=_response.json(),
67
+ ),
68
+ ),
69
+ )
70
+ if _response.status_code == 403:
71
+ raise ForbiddenError(
72
+ headers=dict(_response.headers),
73
+ body=typing.cast(
74
+ AuthorizationError,
75
+ parse_obj_as(
76
+ type_=AuthorizationError, # type: ignore
77
+ object_=_response.json(),
78
+ ),
79
+ ),
80
+ )
81
+ if _response.status_code == 422:
82
+ raise UnprocessableEntityError(
83
+ headers=dict(_response.headers),
84
+ body=typing.cast(
85
+ HttpValidationError,
86
+ parse_obj_as(
87
+ type_=HttpValidationError, # type: ignore
88
+ object_=_response.json(),
89
+ ),
90
+ ),
91
+ )
92
+ _response_json = _response.json()
93
+ except JSONDecodeError:
94
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
95
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
96
+
97
+ def create(
98
+ self,
99
+ *,
100
+ name: str,
101
+ scope: typing.Optional[CreateApiKeyRequestScope] = OMIT,
102
+ expires_at: typing.Optional[dt.datetime] = OMIT,
103
+ request_options: typing.Optional[RequestOptions] = None,
104
+ ) -> HttpResponse[CreateApiKeyResponse]:
105
+ """
106
+ Parameters
107
+ ----------
108
+ name : str
109
+
110
+ scope : typing.Optional[CreateApiKeyRequestScope]
111
+
112
+ expires_at : typing.Optional[dt.datetime]
113
+
114
+ request_options : typing.Optional[RequestOptions]
115
+ Request-specific configuration.
116
+
117
+ Returns
118
+ -------
119
+ HttpResponse[CreateApiKeyResponse]
120
+ Successful Response
121
+ """
122
+ _response = self._client_wrapper.httpx_client.request(
123
+ "api/v1/api-keys",
124
+ method="POST",
125
+ json={
126
+ "name": name,
127
+ "scope": scope,
128
+ "expires_at": expires_at,
129
+ },
130
+ headers={
131
+ "content-type": "application/json",
132
+ },
133
+ request_options=request_options,
134
+ omit=OMIT,
135
+ )
136
+ try:
137
+ if 200 <= _response.status_code < 300:
138
+ _data = typing.cast(
139
+ CreateApiKeyResponse,
140
+ parse_obj_as(
141
+ type_=CreateApiKeyResponse, # type: ignore
142
+ object_=_response.json(),
143
+ ),
144
+ )
145
+ return HttpResponse(response=_response, data=_data)
146
+ if _response.status_code == 401:
147
+ raise UnauthorizedError(
148
+ headers=dict(_response.headers),
149
+ body=typing.cast(
150
+ AuthenticationError,
151
+ parse_obj_as(
152
+ type_=AuthenticationError, # type: ignore
153
+ object_=_response.json(),
154
+ ),
155
+ ),
156
+ )
157
+ if _response.status_code == 403:
158
+ raise ForbiddenError(
159
+ headers=dict(_response.headers),
160
+ body=typing.cast(
161
+ AuthorizationError,
162
+ parse_obj_as(
163
+ type_=AuthorizationError, # type: ignore
164
+ object_=_response.json(),
165
+ ),
166
+ ),
167
+ )
168
+ if _response.status_code == 422:
169
+ raise UnprocessableEntityError(
170
+ headers=dict(_response.headers),
171
+ body=typing.cast(
172
+ HttpValidationError,
173
+ parse_obj_as(
174
+ type_=HttpValidationError, # type: ignore
175
+ object_=_response.json(),
176
+ ),
177
+ ),
178
+ )
179
+ _response_json = _response.json()
180
+ except JSONDecodeError:
181
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
182
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
183
+
184
+ def revoke(
185
+ self, key_id: str, *, request_options: typing.Optional[RequestOptions] = None
186
+ ) -> HttpResponse[RevokeApiKeyResponse]:
187
+ """
188
+ Parameters
189
+ ----------
190
+ key_id : str
191
+ API key UUID
192
+
193
+ request_options : typing.Optional[RequestOptions]
194
+ Request-specific configuration.
195
+
196
+ Returns
197
+ -------
198
+ HttpResponse[RevokeApiKeyResponse]
199
+ Successful Response
200
+ """
201
+ _response = self._client_wrapper.httpx_client.request(
202
+ f"api/v1/api-keys/{jsonable_encoder(key_id)}",
203
+ method="DELETE",
204
+ request_options=request_options,
205
+ )
206
+ try:
207
+ if 200 <= _response.status_code < 300:
208
+ _data = typing.cast(
209
+ RevokeApiKeyResponse,
210
+ parse_obj_as(
211
+ type_=RevokeApiKeyResponse, # type: ignore
212
+ object_=_response.json(),
213
+ ),
214
+ )
215
+ return HttpResponse(response=_response, data=_data)
216
+ if _response.status_code == 401:
217
+ raise UnauthorizedError(
218
+ headers=dict(_response.headers),
219
+ body=typing.cast(
220
+ AuthenticationError,
221
+ parse_obj_as(
222
+ type_=AuthenticationError, # type: ignore
223
+ object_=_response.json(),
224
+ ),
225
+ ),
226
+ )
227
+ if _response.status_code == 403:
228
+ raise ForbiddenError(
229
+ headers=dict(_response.headers),
230
+ body=typing.cast(
231
+ AuthorizationError,
232
+ parse_obj_as(
233
+ type_=AuthorizationError, # type: ignore
234
+ object_=_response.json(),
235
+ ),
236
+ ),
237
+ )
238
+ if _response.status_code == 422:
239
+ raise UnprocessableEntityError(
240
+ headers=dict(_response.headers),
241
+ body=typing.cast(
242
+ HttpValidationError,
243
+ parse_obj_as(
244
+ type_=HttpValidationError, # type: ignore
245
+ object_=_response.json(),
246
+ ),
247
+ ),
248
+ )
249
+ _response_json = _response.json()
250
+ except JSONDecodeError:
251
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
252
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
253
+
254
+ def rotate(
255
+ self, key_id: str, *, request_options: typing.Optional[RequestOptions] = None
256
+ ) -> HttpResponse[CreateApiKeyResponse]:
257
+ """
258
+ Parameters
259
+ ----------
260
+ key_id : str
261
+ API key UUID to rotate
262
+
263
+ request_options : typing.Optional[RequestOptions]
264
+ Request-specific configuration.
265
+
266
+ Returns
267
+ -------
268
+ HttpResponse[CreateApiKeyResponse]
269
+ Successful Response
270
+ """
271
+ _response = self._client_wrapper.httpx_client.request(
272
+ f"api/v1/api-keys/{jsonable_encoder(key_id)}/rotate",
273
+ method="POST",
274
+ request_options=request_options,
275
+ )
276
+ try:
277
+ if 200 <= _response.status_code < 300:
278
+ _data = typing.cast(
279
+ CreateApiKeyResponse,
280
+ parse_obj_as(
281
+ type_=CreateApiKeyResponse, # type: ignore
282
+ object_=_response.json(),
283
+ ),
284
+ )
285
+ return HttpResponse(response=_response, data=_data)
286
+ if _response.status_code == 401:
287
+ raise UnauthorizedError(
288
+ headers=dict(_response.headers),
289
+ body=typing.cast(
290
+ AuthenticationError,
291
+ parse_obj_as(
292
+ type_=AuthenticationError, # type: ignore
293
+ object_=_response.json(),
294
+ ),
295
+ ),
296
+ )
297
+ if _response.status_code == 403:
298
+ raise ForbiddenError(
299
+ headers=dict(_response.headers),
300
+ body=typing.cast(
301
+ AuthorizationError,
302
+ parse_obj_as(
303
+ type_=AuthorizationError, # type: ignore
304
+ object_=_response.json(),
305
+ ),
306
+ ),
307
+ )
308
+ if _response.status_code == 422:
309
+ raise UnprocessableEntityError(
310
+ headers=dict(_response.headers),
311
+ body=typing.cast(
312
+ HttpValidationError,
313
+ parse_obj_as(
314
+ type_=HttpValidationError, # type: ignore
315
+ object_=_response.json(),
316
+ ),
317
+ ),
318
+ )
319
+ _response_json = _response.json()
320
+ except JSONDecodeError:
321
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
322
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
323
+
324
+
325
+ class AsyncRawApiKeysClient:
326
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
327
+ self._client_wrapper = client_wrapper
328
+
329
+ async def list(
330
+ self, *, request_options: typing.Optional[RequestOptions] = None
331
+ ) -> AsyncHttpResponse[ListApiKeysResponse]:
332
+ """
333
+ Parameters
334
+ ----------
335
+ request_options : typing.Optional[RequestOptions]
336
+ Request-specific configuration.
337
+
338
+ Returns
339
+ -------
340
+ AsyncHttpResponse[ListApiKeysResponse]
341
+ Successful Response
342
+ """
343
+ _response = await self._client_wrapper.httpx_client.request(
344
+ "api/v1/api-keys",
345
+ method="GET",
346
+ request_options=request_options,
347
+ )
348
+ try:
349
+ if 200 <= _response.status_code < 300:
350
+ _data = typing.cast(
351
+ ListApiKeysResponse,
352
+ parse_obj_as(
353
+ type_=ListApiKeysResponse, # type: ignore
354
+ object_=_response.json(),
355
+ ),
356
+ )
357
+ return AsyncHttpResponse(response=_response, data=_data)
358
+ if _response.status_code == 401:
359
+ raise UnauthorizedError(
360
+ headers=dict(_response.headers),
361
+ body=typing.cast(
362
+ AuthenticationError,
363
+ parse_obj_as(
364
+ type_=AuthenticationError, # type: ignore
365
+ object_=_response.json(),
366
+ ),
367
+ ),
368
+ )
369
+ if _response.status_code == 403:
370
+ raise ForbiddenError(
371
+ headers=dict(_response.headers),
372
+ body=typing.cast(
373
+ AuthorizationError,
374
+ parse_obj_as(
375
+ type_=AuthorizationError, # type: ignore
376
+ object_=_response.json(),
377
+ ),
378
+ ),
379
+ )
380
+ if _response.status_code == 422:
381
+ raise UnprocessableEntityError(
382
+ headers=dict(_response.headers),
383
+ body=typing.cast(
384
+ HttpValidationError,
385
+ parse_obj_as(
386
+ type_=HttpValidationError, # type: ignore
387
+ object_=_response.json(),
388
+ ),
389
+ ),
390
+ )
391
+ _response_json = _response.json()
392
+ except JSONDecodeError:
393
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
394
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
395
+
396
+ async def create(
397
+ self,
398
+ *,
399
+ name: str,
400
+ scope: typing.Optional[CreateApiKeyRequestScope] = OMIT,
401
+ expires_at: typing.Optional[dt.datetime] = OMIT,
402
+ request_options: typing.Optional[RequestOptions] = None,
403
+ ) -> AsyncHttpResponse[CreateApiKeyResponse]:
404
+ """
405
+ Parameters
406
+ ----------
407
+ name : str
408
+
409
+ scope : typing.Optional[CreateApiKeyRequestScope]
410
+
411
+ expires_at : typing.Optional[dt.datetime]
412
+
413
+ request_options : typing.Optional[RequestOptions]
414
+ Request-specific configuration.
415
+
416
+ Returns
417
+ -------
418
+ AsyncHttpResponse[CreateApiKeyResponse]
419
+ Successful Response
420
+ """
421
+ _response = await self._client_wrapper.httpx_client.request(
422
+ "api/v1/api-keys",
423
+ method="POST",
424
+ json={
425
+ "name": name,
426
+ "scope": scope,
427
+ "expires_at": expires_at,
428
+ },
429
+ headers={
430
+ "content-type": "application/json",
431
+ },
432
+ request_options=request_options,
433
+ omit=OMIT,
434
+ )
435
+ try:
436
+ if 200 <= _response.status_code < 300:
437
+ _data = typing.cast(
438
+ CreateApiKeyResponse,
439
+ parse_obj_as(
440
+ type_=CreateApiKeyResponse, # type: ignore
441
+ object_=_response.json(),
442
+ ),
443
+ )
444
+ return AsyncHttpResponse(response=_response, data=_data)
445
+ if _response.status_code == 401:
446
+ raise UnauthorizedError(
447
+ headers=dict(_response.headers),
448
+ body=typing.cast(
449
+ AuthenticationError,
450
+ parse_obj_as(
451
+ type_=AuthenticationError, # type: ignore
452
+ object_=_response.json(),
453
+ ),
454
+ ),
455
+ )
456
+ if _response.status_code == 403:
457
+ raise ForbiddenError(
458
+ headers=dict(_response.headers),
459
+ body=typing.cast(
460
+ AuthorizationError,
461
+ parse_obj_as(
462
+ type_=AuthorizationError, # type: ignore
463
+ object_=_response.json(),
464
+ ),
465
+ ),
466
+ )
467
+ if _response.status_code == 422:
468
+ raise UnprocessableEntityError(
469
+ headers=dict(_response.headers),
470
+ body=typing.cast(
471
+ HttpValidationError,
472
+ parse_obj_as(
473
+ type_=HttpValidationError, # type: ignore
474
+ object_=_response.json(),
475
+ ),
476
+ ),
477
+ )
478
+ _response_json = _response.json()
479
+ except JSONDecodeError:
480
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
481
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
482
+
483
+ async def revoke(
484
+ self, key_id: str, *, request_options: typing.Optional[RequestOptions] = None
485
+ ) -> AsyncHttpResponse[RevokeApiKeyResponse]:
486
+ """
487
+ Parameters
488
+ ----------
489
+ key_id : str
490
+ API key UUID
491
+
492
+ request_options : typing.Optional[RequestOptions]
493
+ Request-specific configuration.
494
+
495
+ Returns
496
+ -------
497
+ AsyncHttpResponse[RevokeApiKeyResponse]
498
+ Successful Response
499
+ """
500
+ _response = await self._client_wrapper.httpx_client.request(
501
+ f"api/v1/api-keys/{jsonable_encoder(key_id)}",
502
+ method="DELETE",
503
+ request_options=request_options,
504
+ )
505
+ try:
506
+ if 200 <= _response.status_code < 300:
507
+ _data = typing.cast(
508
+ RevokeApiKeyResponse,
509
+ parse_obj_as(
510
+ type_=RevokeApiKeyResponse, # type: ignore
511
+ object_=_response.json(),
512
+ ),
513
+ )
514
+ return AsyncHttpResponse(response=_response, data=_data)
515
+ if _response.status_code == 401:
516
+ raise UnauthorizedError(
517
+ headers=dict(_response.headers),
518
+ body=typing.cast(
519
+ AuthenticationError,
520
+ parse_obj_as(
521
+ type_=AuthenticationError, # type: ignore
522
+ object_=_response.json(),
523
+ ),
524
+ ),
525
+ )
526
+ if _response.status_code == 403:
527
+ raise ForbiddenError(
528
+ headers=dict(_response.headers),
529
+ body=typing.cast(
530
+ AuthorizationError,
531
+ parse_obj_as(
532
+ type_=AuthorizationError, # type: ignore
533
+ object_=_response.json(),
534
+ ),
535
+ ),
536
+ )
537
+ if _response.status_code == 422:
538
+ raise UnprocessableEntityError(
539
+ headers=dict(_response.headers),
540
+ body=typing.cast(
541
+ HttpValidationError,
542
+ parse_obj_as(
543
+ type_=HttpValidationError, # type: ignore
544
+ object_=_response.json(),
545
+ ),
546
+ ),
547
+ )
548
+ _response_json = _response.json()
549
+ except JSONDecodeError:
550
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
551
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
552
+
553
+ async def rotate(
554
+ self, key_id: str, *, request_options: typing.Optional[RequestOptions] = None
555
+ ) -> AsyncHttpResponse[CreateApiKeyResponse]:
556
+ """
557
+ Parameters
558
+ ----------
559
+ key_id : str
560
+ API key UUID to rotate
561
+
562
+ request_options : typing.Optional[RequestOptions]
563
+ Request-specific configuration.
564
+
565
+ Returns
566
+ -------
567
+ AsyncHttpResponse[CreateApiKeyResponse]
568
+ Successful Response
569
+ """
570
+ _response = await self._client_wrapper.httpx_client.request(
571
+ f"api/v1/api-keys/{jsonable_encoder(key_id)}/rotate",
572
+ method="POST",
573
+ request_options=request_options,
574
+ )
575
+ try:
576
+ if 200 <= _response.status_code < 300:
577
+ _data = typing.cast(
578
+ CreateApiKeyResponse,
579
+ parse_obj_as(
580
+ type_=CreateApiKeyResponse, # type: ignore
581
+ object_=_response.json(),
582
+ ),
583
+ )
584
+ return AsyncHttpResponse(response=_response, data=_data)
585
+ if _response.status_code == 401:
586
+ raise UnauthorizedError(
587
+ headers=dict(_response.headers),
588
+ body=typing.cast(
589
+ AuthenticationError,
590
+ parse_obj_as(
591
+ type_=AuthenticationError, # type: ignore
592
+ object_=_response.json(),
593
+ ),
594
+ ),
595
+ )
596
+ if _response.status_code == 403:
597
+ raise ForbiddenError(
598
+ headers=dict(_response.headers),
599
+ body=typing.cast(
600
+ AuthorizationError,
601
+ parse_obj_as(
602
+ type_=AuthorizationError, # type: ignore
603
+ object_=_response.json(),
604
+ ),
605
+ ),
606
+ )
607
+ if _response.status_code == 422:
608
+ raise UnprocessableEntityError(
609
+ headers=dict(_response.headers),
610
+ body=typing.cast(
611
+ HttpValidationError,
612
+ parse_obj_as(
613
+ type_=HttpValidationError, # type: ignore
614
+ object_=_response.json(),
615
+ ),
616
+ ),
617
+ )
618
+ _response_json = _response.json()
619
+ except JSONDecodeError:
620
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
621
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
@@ -0,0 +1,34 @@
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 .create_api_key_request_scope import CreateApiKeyRequestScope
10
+ _dynamic_imports: typing.Dict[str, str] = {"CreateApiKeyRequestScope": ".create_api_key_request_scope"}
11
+
12
+
13
+ def __getattr__(attr_name: str) -> typing.Any:
14
+ module_name = _dynamic_imports.get(attr_name)
15
+ if module_name is None:
16
+ raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}")
17
+ try:
18
+ module = import_module(module_name, __package__)
19
+ if module_name == f".{attr_name}":
20
+ return module
21
+ else:
22
+ return getattr(module, attr_name)
23
+ except ImportError as e:
24
+ raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e
25
+ except AttributeError as e:
26
+ raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e
27
+
28
+
29
+ def __dir__():
30
+ lazy_attrs = list(_dynamic_imports.keys())
31
+ return sorted(lazy_attrs)
32
+
33
+
34
+ __all__ = ["CreateApiKeyRequestScope"]
@@ -0,0 +1,5 @@
1
+ # This file was auto-generated by Fern from our API Definition.
2
+
3
+ import typing
4
+
5
+ CreateApiKeyRequestScope = typing.Union[typing.Literal["read", "read-write"], typing.Any]
@@ -0,0 +1,39 @@
1
+ import os
2
+
3
+ from .base_client import AsyncBaseLevelFour
4
+ from .exceptions import AuthenticationError
5
+
6
+ _VALID_PREFIXES = ("l4_live_", "l4_test_")
7
+ _DEFAULT_BASE_URL = "https://api.levelfour.ai"
8
+
9
+
10
+ def _resolve_api_key(api_key: str | None) -> str:
11
+ key = api_key or os.environ.get("LEVELFOUR_API_KEY")
12
+ if not key:
13
+ raise AuthenticationError(
14
+ "No API key provided. Set LEVELFOUR_API_KEY environment variable or pass api_key= to the constructor."
15
+ )
16
+ if not key.startswith(_VALID_PREFIXES):
17
+ raise AuthenticationError(
18
+ f"Invalid API key format. Key must start with one of: {', '.join(_VALID_PREFIXES)}"
19
+ )
20
+ return key
21
+
22
+
23
+ class AsyncLevelFour(AsyncBaseLevelFour):
24
+ def __init__(
25
+ self,
26
+ *,
27
+ api_key: str | None = None,
28
+ base_url: str = _DEFAULT_BASE_URL,
29
+ timeout: float = 30.0,
30
+ max_retries: int = 2,
31
+ ):
32
+ resolved_key = _resolve_api_key(api_key)
33
+ super().__init__(
34
+ base_url=base_url,
35
+ token=resolved_key,
36
+ timeout=timeout,
37
+ )
38
+ self._api_key = resolved_key
39
+ self._max_retries = max_retries