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,1810 @@
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 jsonable_encoder
10
+ from ..core.pagination import AsyncPager, SyncPager
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.accept_terms_response import AcceptTermsResponse
17
+ from ..types.authentication_error import AuthenticationError
18
+ from ..types.authorization_error import AuthorizationError
19
+ from ..types.complete_wizard_response import CompleteWizardResponse
20
+ from ..types.http_validation_error import HttpValidationError
21
+ from ..types.implementation_method_response import ImplementationMethodResponse
22
+ from ..types.processing_recommendations_response import ProcessingRecommendationsResponse
23
+ from ..types.providers_summary_response import ProvidersSummaryResponse
24
+ from ..types.recommendation_detail_response import RecommendationDetailResponse
25
+ from ..types.recommendation_item import RecommendationItem
26
+ from ..types.recommendation_wizard_response import RecommendationWizardResponse
27
+ from ..types.recommendations_breakdown_response import RecommendationsBreakdownResponse
28
+ from ..types.recommendations_overview_response import RecommendationsOverviewResponse
29
+ from ..types.savings_decision_response import SavingsDecisionResponse
30
+ from ..types.update_tag_response import UpdateTagResponse
31
+
32
+ # this is used as the default value for optional parameters
33
+ OMIT = typing.cast(typing.Any, ...)
34
+
35
+
36
+ class RawRecommendationsClient:
37
+ def __init__(self, *, client_wrapper: SyncClientWrapper):
38
+ self._client_wrapper = client_wrapper
39
+
40
+ def get_summary(
41
+ self, *, request_options: typing.Optional[RequestOptions] = None
42
+ ) -> HttpResponse[ProvidersSummaryResponse]:
43
+ """
44
+ Returns provider-level aggregation for the carousel view (AWS, GCP, Azure, K8s)
45
+
46
+ Parameters
47
+ ----------
48
+ request_options : typing.Optional[RequestOptions]
49
+ Request-specific configuration.
50
+
51
+ Returns
52
+ -------
53
+ HttpResponse[ProvidersSummaryResponse]
54
+ Successful Response
55
+ """
56
+ _response = self._client_wrapper.httpx_client.request(
57
+ "api/v1/recommendations/summary",
58
+ method="GET",
59
+ request_options=request_options,
60
+ )
61
+ try:
62
+ if 200 <= _response.status_code < 300:
63
+ _data = typing.cast(
64
+ ProvidersSummaryResponse,
65
+ parse_obj_as(
66
+ type_=ProvidersSummaryResponse, # type: ignore
67
+ object_=_response.json(),
68
+ ),
69
+ )
70
+ return HttpResponse(response=_response, data=_data)
71
+ if _response.status_code == 401:
72
+ raise UnauthorizedError(
73
+ headers=dict(_response.headers),
74
+ body=typing.cast(
75
+ AuthenticationError,
76
+ parse_obj_as(
77
+ type_=AuthenticationError, # type: ignore
78
+ object_=_response.json(),
79
+ ),
80
+ ),
81
+ )
82
+ if _response.status_code == 403:
83
+ raise ForbiddenError(
84
+ headers=dict(_response.headers),
85
+ body=typing.cast(
86
+ AuthorizationError,
87
+ parse_obj_as(
88
+ type_=AuthorizationError, # type: ignore
89
+ object_=_response.json(),
90
+ ),
91
+ ),
92
+ )
93
+ _response_json = _response.json()
94
+ except JSONDecodeError:
95
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
96
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
97
+
98
+ def get_potential_savings_summary(
99
+ self, *, request_options: typing.Optional[RequestOptions] = None
100
+ ) -> HttpResponse[ProvidersSummaryResponse]:
101
+ """
102
+ Returns potential savings summary grouped by provider (from dashboard_potential_savings)
103
+
104
+ Parameters
105
+ ----------
106
+ request_options : typing.Optional[RequestOptions]
107
+ Request-specific configuration.
108
+
109
+ Returns
110
+ -------
111
+ HttpResponse[ProvidersSummaryResponse]
112
+ Successful Response
113
+ """
114
+ _response = self._client_wrapper.httpx_client.request(
115
+ "api/v1/recommendations/potential-savings/summary",
116
+ method="GET",
117
+ request_options=request_options,
118
+ )
119
+ try:
120
+ if 200 <= _response.status_code < 300:
121
+ _data = typing.cast(
122
+ ProvidersSummaryResponse,
123
+ parse_obj_as(
124
+ type_=ProvidersSummaryResponse, # type: ignore
125
+ object_=_response.json(),
126
+ ),
127
+ )
128
+ return HttpResponse(response=_response, data=_data)
129
+ if _response.status_code == 401:
130
+ raise UnauthorizedError(
131
+ headers=dict(_response.headers),
132
+ body=typing.cast(
133
+ AuthenticationError,
134
+ parse_obj_as(
135
+ type_=AuthenticationError, # type: ignore
136
+ object_=_response.json(),
137
+ ),
138
+ ),
139
+ )
140
+ if _response.status_code == 403:
141
+ raise ForbiddenError(
142
+ headers=dict(_response.headers),
143
+ body=typing.cast(
144
+ AuthorizationError,
145
+ parse_obj_as(
146
+ type_=AuthorizationError, # type: ignore
147
+ object_=_response.json(),
148
+ ),
149
+ ),
150
+ )
151
+ _response_json = _response.json()
152
+ except JSONDecodeError:
153
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
154
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
155
+
156
+ def list(
157
+ self,
158
+ *,
159
+ page: typing.Optional[int] = None,
160
+ page_size: typing.Optional[int] = None,
161
+ sort_by: typing.Optional[str] = None,
162
+ sort_order: typing.Optional[str] = None,
163
+ request_options: typing.Optional[RequestOptions] = None,
164
+ ) -> SyncPager[RecommendationItem, RecommendationsBreakdownResponse]:
165
+ """
166
+ Returns paginated list of all recommendations with actions
167
+
168
+ Parameters
169
+ ----------
170
+ page : typing.Optional[int]
171
+ Page number (1-indexed)
172
+
173
+ page_size : typing.Optional[int]
174
+ Items per page (max 100)
175
+
176
+ sort_by : typing.Optional[str]
177
+ Field to sort by (recommendation_id, service, environment, monthly_savings, annual_savings, savings_percentage, analysis_period, created_at)
178
+
179
+ sort_order : typing.Optional[str]
180
+ Sort order (asc or desc)
181
+
182
+ request_options : typing.Optional[RequestOptions]
183
+ Request-specific configuration.
184
+
185
+ Returns
186
+ -------
187
+ SyncPager[RecommendationItem, RecommendationsBreakdownResponse]
188
+ Successful Response
189
+ """
190
+ page = page if page is not None else 1
191
+
192
+ _response = self._client_wrapper.httpx_client.request(
193
+ "api/v1/recommendations",
194
+ method="GET",
195
+ params={
196
+ "page": page,
197
+ "page_size": page_size,
198
+ "sort_by": sort_by,
199
+ "sort_order": sort_order,
200
+ },
201
+ request_options=request_options,
202
+ )
203
+ try:
204
+ if 200 <= _response.status_code < 300:
205
+ _parsed_response = typing.cast(
206
+ RecommendationsBreakdownResponse,
207
+ parse_obj_as(
208
+ type_=RecommendationsBreakdownResponse, # type: ignore
209
+ object_=_response.json(),
210
+ ),
211
+ )
212
+ _items = _parsed_response.data.items if _parsed_response.data is not None else []
213
+
214
+ _has_next = True
215
+ _get_next = lambda: self.list(
216
+ page=page + 1,
217
+ page_size=page_size,
218
+ sort_by=sort_by,
219
+ sort_order=sort_order,
220
+ request_options=request_options,
221
+ )
222
+ return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
223
+ if _response.status_code == 401:
224
+ raise UnauthorizedError(
225
+ headers=dict(_response.headers),
226
+ body=typing.cast(
227
+ AuthenticationError,
228
+ parse_obj_as(
229
+ type_=AuthenticationError, # type: ignore
230
+ object_=_response.json(),
231
+ ),
232
+ ),
233
+ )
234
+ if _response.status_code == 403:
235
+ raise ForbiddenError(
236
+ headers=dict(_response.headers),
237
+ body=typing.cast(
238
+ AuthorizationError,
239
+ parse_obj_as(
240
+ type_=AuthorizationError, # type: ignore
241
+ object_=_response.json(),
242
+ ),
243
+ ),
244
+ )
245
+ if _response.status_code == 422:
246
+ raise UnprocessableEntityError(
247
+ headers=dict(_response.headers),
248
+ body=typing.cast(
249
+ HttpValidationError,
250
+ parse_obj_as(
251
+ type_=HttpValidationError, # type: ignore
252
+ object_=_response.json(),
253
+ ),
254
+ ),
255
+ )
256
+ _response_json = _response.json()
257
+ except JSONDecodeError:
258
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
259
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
260
+
261
+ def get_overview(
262
+ self, *, request_options: typing.Optional[RequestOptions] = None
263
+ ) -> HttpResponse[RecommendationsOverviewResponse]:
264
+ """
265
+ Returns recommendations overview metrics across all providers (for dashboard)
266
+
267
+ Parameters
268
+ ----------
269
+ request_options : typing.Optional[RequestOptions]
270
+ Request-specific configuration.
271
+
272
+ Returns
273
+ -------
274
+ HttpResponse[RecommendationsOverviewResponse]
275
+ Successful Response
276
+ """
277
+ _response = self._client_wrapper.httpx_client.request(
278
+ "api/v1/recommendations/overview",
279
+ method="GET",
280
+ request_options=request_options,
281
+ )
282
+ try:
283
+ if 200 <= _response.status_code < 300:
284
+ _data = typing.cast(
285
+ RecommendationsOverviewResponse,
286
+ parse_obj_as(
287
+ type_=RecommendationsOverviewResponse, # type: ignore
288
+ object_=_response.json(),
289
+ ),
290
+ )
291
+ return HttpResponse(response=_response, data=_data)
292
+ if _response.status_code == 401:
293
+ raise UnauthorizedError(
294
+ headers=dict(_response.headers),
295
+ body=typing.cast(
296
+ AuthenticationError,
297
+ parse_obj_as(
298
+ type_=AuthenticationError, # type: ignore
299
+ object_=_response.json(),
300
+ ),
301
+ ),
302
+ )
303
+ if _response.status_code == 403:
304
+ raise ForbiddenError(
305
+ headers=dict(_response.headers),
306
+ body=typing.cast(
307
+ AuthorizationError,
308
+ parse_obj_as(
309
+ type_=AuthorizationError, # type: ignore
310
+ object_=_response.json(),
311
+ ),
312
+ ),
313
+ )
314
+ _response_json = _response.json()
315
+ except JSONDecodeError:
316
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
317
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
318
+
319
+ def get_processing(
320
+ self, *, request_options: typing.Optional[RequestOptions] = None
321
+ ) -> HttpResponse[ProcessingRecommendationsResponse]:
322
+ """
323
+ Returns all recommendations currently in processing status
324
+
325
+ Parameters
326
+ ----------
327
+ request_options : typing.Optional[RequestOptions]
328
+ Request-specific configuration.
329
+
330
+ Returns
331
+ -------
332
+ HttpResponse[ProcessingRecommendationsResponse]
333
+ Successful Response
334
+ """
335
+ _response = self._client_wrapper.httpx_client.request(
336
+ "api/v1/recommendations/processing",
337
+ method="GET",
338
+ request_options=request_options,
339
+ )
340
+ try:
341
+ if 200 <= _response.status_code < 300:
342
+ _data = typing.cast(
343
+ ProcessingRecommendationsResponse,
344
+ parse_obj_as(
345
+ type_=ProcessingRecommendationsResponse, # type: ignore
346
+ object_=_response.json(),
347
+ ),
348
+ )
349
+ return HttpResponse(response=_response, data=_data)
350
+ if _response.status_code == 401:
351
+ raise UnauthorizedError(
352
+ headers=dict(_response.headers),
353
+ body=typing.cast(
354
+ AuthenticationError,
355
+ parse_obj_as(
356
+ type_=AuthenticationError, # type: ignore
357
+ object_=_response.json(),
358
+ ),
359
+ ),
360
+ )
361
+ if _response.status_code == 403:
362
+ raise ForbiddenError(
363
+ headers=dict(_response.headers),
364
+ body=typing.cast(
365
+ AuthorizationError,
366
+ parse_obj_as(
367
+ type_=AuthorizationError, # type: ignore
368
+ object_=_response.json(),
369
+ ),
370
+ ),
371
+ )
372
+ _response_json = _response.json()
373
+ except JSONDecodeError:
374
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
375
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
376
+
377
+ def get_details(
378
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
379
+ ) -> HttpResponse[RecommendationDetailResponse]:
380
+ """
381
+ Returns complete details for a specific recommendation
382
+
383
+ Parameters
384
+ ----------
385
+ recommendation_id : str
386
+
387
+ request_options : typing.Optional[RequestOptions]
388
+ Request-specific configuration.
389
+
390
+ Returns
391
+ -------
392
+ HttpResponse[RecommendationDetailResponse]
393
+ Successful Response
394
+ """
395
+ _response = self._client_wrapper.httpx_client.request(
396
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/details",
397
+ method="GET",
398
+ request_options=request_options,
399
+ )
400
+ try:
401
+ if 200 <= _response.status_code < 300:
402
+ _data = typing.cast(
403
+ RecommendationDetailResponse,
404
+ parse_obj_as(
405
+ type_=RecommendationDetailResponse, # type: ignore
406
+ object_=_response.json(),
407
+ ),
408
+ )
409
+ return HttpResponse(response=_response, data=_data)
410
+ if _response.status_code == 401:
411
+ raise UnauthorizedError(
412
+ headers=dict(_response.headers),
413
+ body=typing.cast(
414
+ AuthenticationError,
415
+ parse_obj_as(
416
+ type_=AuthenticationError, # type: ignore
417
+ object_=_response.json(),
418
+ ),
419
+ ),
420
+ )
421
+ if _response.status_code == 403:
422
+ raise ForbiddenError(
423
+ headers=dict(_response.headers),
424
+ body=typing.cast(
425
+ AuthorizationError,
426
+ parse_obj_as(
427
+ type_=AuthorizationError, # type: ignore
428
+ object_=_response.json(),
429
+ ),
430
+ ),
431
+ )
432
+ if _response.status_code == 422:
433
+ raise UnprocessableEntityError(
434
+ headers=dict(_response.headers),
435
+ body=typing.cast(
436
+ HttpValidationError,
437
+ parse_obj_as(
438
+ type_=HttpValidationError, # type: ignore
439
+ object_=_response.json(),
440
+ ),
441
+ ),
442
+ )
443
+ _response_json = _response.json()
444
+ except JSONDecodeError:
445
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
446
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
447
+
448
+ def get_wizard(
449
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
450
+ ) -> HttpResponse[RecommendationWizardResponse]:
451
+ """
452
+ Returns recommendation details for the Save Now wizard flow
453
+
454
+ Parameters
455
+ ----------
456
+ recommendation_id : str
457
+
458
+ request_options : typing.Optional[RequestOptions]
459
+ Request-specific configuration.
460
+
461
+ Returns
462
+ -------
463
+ HttpResponse[RecommendationWizardResponse]
464
+ Successful Response
465
+ """
466
+ _response = self._client_wrapper.httpx_client.request(
467
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}",
468
+ method="GET",
469
+ request_options=request_options,
470
+ )
471
+ try:
472
+ if 200 <= _response.status_code < 300:
473
+ _data = typing.cast(
474
+ RecommendationWizardResponse,
475
+ parse_obj_as(
476
+ type_=RecommendationWizardResponse, # type: ignore
477
+ object_=_response.json(),
478
+ ),
479
+ )
480
+ return HttpResponse(response=_response, data=_data)
481
+ if _response.status_code == 401:
482
+ raise UnauthorizedError(
483
+ headers=dict(_response.headers),
484
+ body=typing.cast(
485
+ AuthenticationError,
486
+ parse_obj_as(
487
+ type_=AuthenticationError, # type: ignore
488
+ object_=_response.json(),
489
+ ),
490
+ ),
491
+ )
492
+ if _response.status_code == 403:
493
+ raise ForbiddenError(
494
+ headers=dict(_response.headers),
495
+ body=typing.cast(
496
+ AuthorizationError,
497
+ parse_obj_as(
498
+ type_=AuthorizationError, # type: ignore
499
+ object_=_response.json(),
500
+ ),
501
+ ),
502
+ )
503
+ if _response.status_code == 422:
504
+ raise UnprocessableEntityError(
505
+ headers=dict(_response.headers),
506
+ body=typing.cast(
507
+ HttpValidationError,
508
+ parse_obj_as(
509
+ type_=HttpValidationError, # type: ignore
510
+ object_=_response.json(),
511
+ ),
512
+ ),
513
+ )
514
+ _response_json = _response.json()
515
+ except JSONDecodeError:
516
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
517
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
518
+
519
+ def accept_terms(
520
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
521
+ ) -> HttpResponse[AcceptTermsResponse]:
522
+ """
523
+ Records user acceptance of terms and conditions for a recommendation
524
+
525
+ Parameters
526
+ ----------
527
+ recommendation_id : str
528
+
529
+ request_options : typing.Optional[RequestOptions]
530
+ Request-specific configuration.
531
+
532
+ Returns
533
+ -------
534
+ HttpResponse[AcceptTermsResponse]
535
+ Successful Response
536
+ """
537
+ _response = self._client_wrapper.httpx_client.request(
538
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/accept-terms",
539
+ method="POST",
540
+ request_options=request_options,
541
+ )
542
+ try:
543
+ if 200 <= _response.status_code < 300:
544
+ _data = typing.cast(
545
+ AcceptTermsResponse,
546
+ parse_obj_as(
547
+ type_=AcceptTermsResponse, # type: ignore
548
+ object_=_response.json(),
549
+ ),
550
+ )
551
+ return HttpResponse(response=_response, data=_data)
552
+ if _response.status_code == 401:
553
+ raise UnauthorizedError(
554
+ headers=dict(_response.headers),
555
+ body=typing.cast(
556
+ AuthenticationError,
557
+ parse_obj_as(
558
+ type_=AuthenticationError, # type: ignore
559
+ object_=_response.json(),
560
+ ),
561
+ ),
562
+ )
563
+ if _response.status_code == 403:
564
+ raise ForbiddenError(
565
+ headers=dict(_response.headers),
566
+ body=typing.cast(
567
+ AuthorizationError,
568
+ parse_obj_as(
569
+ type_=AuthorizationError, # type: ignore
570
+ object_=_response.json(),
571
+ ),
572
+ ),
573
+ )
574
+ if _response.status_code == 422:
575
+ raise UnprocessableEntityError(
576
+ headers=dict(_response.headers),
577
+ body=typing.cast(
578
+ HttpValidationError,
579
+ parse_obj_as(
580
+ type_=HttpValidationError, # type: ignore
581
+ object_=_response.json(),
582
+ ),
583
+ ),
584
+ )
585
+ _response_json = _response.json()
586
+ except JSONDecodeError:
587
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
588
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
589
+
590
+ def update_tag(
591
+ self,
592
+ recommendation_id: str,
593
+ *,
594
+ tag: typing.Optional[str] = OMIT,
595
+ request_options: typing.Optional[RequestOptions] = None,
596
+ ) -> HttpResponse[UpdateTagResponse]:
597
+ """
598
+ Updates the tag value for a recommendation
599
+
600
+ Parameters
601
+ ----------
602
+ recommendation_id : str
603
+
604
+ tag : typing.Optional[str]
605
+ New tag value. Send null to clear.
606
+
607
+ request_options : typing.Optional[RequestOptions]
608
+ Request-specific configuration.
609
+
610
+ Returns
611
+ -------
612
+ HttpResponse[UpdateTagResponse]
613
+ Successful Response
614
+ """
615
+ _response = self._client_wrapper.httpx_client.request(
616
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/tag",
617
+ method="POST",
618
+ json={
619
+ "tag": tag,
620
+ },
621
+ headers={
622
+ "content-type": "application/json",
623
+ },
624
+ request_options=request_options,
625
+ omit=OMIT,
626
+ )
627
+ try:
628
+ if 200 <= _response.status_code < 300:
629
+ _data = typing.cast(
630
+ UpdateTagResponse,
631
+ parse_obj_as(
632
+ type_=UpdateTagResponse, # type: ignore
633
+ object_=_response.json(),
634
+ ),
635
+ )
636
+ return HttpResponse(response=_response, data=_data)
637
+ if _response.status_code == 401:
638
+ raise UnauthorizedError(
639
+ headers=dict(_response.headers),
640
+ body=typing.cast(
641
+ AuthenticationError,
642
+ parse_obj_as(
643
+ type_=AuthenticationError, # type: ignore
644
+ object_=_response.json(),
645
+ ),
646
+ ),
647
+ )
648
+ if _response.status_code == 403:
649
+ raise ForbiddenError(
650
+ headers=dict(_response.headers),
651
+ body=typing.cast(
652
+ AuthorizationError,
653
+ parse_obj_as(
654
+ type_=AuthorizationError, # type: ignore
655
+ object_=_response.json(),
656
+ ),
657
+ ),
658
+ )
659
+ if _response.status_code == 422:
660
+ raise UnprocessableEntityError(
661
+ headers=dict(_response.headers),
662
+ body=typing.cast(
663
+ HttpValidationError,
664
+ parse_obj_as(
665
+ type_=HttpValidationError, # type: ignore
666
+ object_=_response.json(),
667
+ ),
668
+ ),
669
+ )
670
+ _response_json = _response.json()
671
+ except JSONDecodeError:
672
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
673
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
674
+
675
+ def make_decision(
676
+ self,
677
+ recommendation_id: str,
678
+ *,
679
+ decision: str,
680
+ reason: typing.Optional[str] = OMIT,
681
+ explanation: typing.Optional[str] = OMIT,
682
+ request_options: typing.Optional[RequestOptions] = None,
683
+ ) -> HttpResponse[SavingsDecisionResponse]:
684
+ """
685
+ Records user decision to accept or reject a savings recommendation
686
+
687
+ Parameters
688
+ ----------
689
+ recommendation_id : str
690
+
691
+ decision : str
692
+ Decision: 'accepted' or 'rejected'
693
+
694
+ reason : typing.Optional[str]
695
+ Rejection reason: operational, strategy, not_applicable, other (required if rejected)
696
+
697
+ explanation : typing.Optional[str]
698
+ Free text explanation (optional, used when reason is 'other')
699
+
700
+ request_options : typing.Optional[RequestOptions]
701
+ Request-specific configuration.
702
+
703
+ Returns
704
+ -------
705
+ HttpResponse[SavingsDecisionResponse]
706
+ Successful Response
707
+ """
708
+ _response = self._client_wrapper.httpx_client.request(
709
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/decision",
710
+ method="POST",
711
+ json={
712
+ "decision": decision,
713
+ "reason": reason,
714
+ "explanation": explanation,
715
+ },
716
+ headers={
717
+ "content-type": "application/json",
718
+ },
719
+ request_options=request_options,
720
+ omit=OMIT,
721
+ )
722
+ try:
723
+ if 200 <= _response.status_code < 300:
724
+ _data = typing.cast(
725
+ SavingsDecisionResponse,
726
+ parse_obj_as(
727
+ type_=SavingsDecisionResponse, # type: ignore
728
+ object_=_response.json(),
729
+ ),
730
+ )
731
+ return HttpResponse(response=_response, data=_data)
732
+ if _response.status_code == 401:
733
+ raise UnauthorizedError(
734
+ headers=dict(_response.headers),
735
+ body=typing.cast(
736
+ AuthenticationError,
737
+ parse_obj_as(
738
+ type_=AuthenticationError, # type: ignore
739
+ object_=_response.json(),
740
+ ),
741
+ ),
742
+ )
743
+ if _response.status_code == 403:
744
+ raise ForbiddenError(
745
+ headers=dict(_response.headers),
746
+ body=typing.cast(
747
+ AuthorizationError,
748
+ parse_obj_as(
749
+ type_=AuthorizationError, # type: ignore
750
+ object_=_response.json(),
751
+ ),
752
+ ),
753
+ )
754
+ if _response.status_code == 422:
755
+ raise UnprocessableEntityError(
756
+ headers=dict(_response.headers),
757
+ body=typing.cast(
758
+ HttpValidationError,
759
+ parse_obj_as(
760
+ type_=HttpValidationError, # type: ignore
761
+ object_=_response.json(),
762
+ ),
763
+ ),
764
+ )
765
+ _response_json = _response.json()
766
+ except JSONDecodeError:
767
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
768
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
769
+
770
+ def set_implementation(
771
+ self, recommendation_id: str, *, method: str, request_options: typing.Optional[RequestOptions] = None
772
+ ) -> HttpResponse[ImplementationMethodResponse]:
773
+ """
774
+ Records the chosen implementation method for a recommendation
775
+
776
+ Parameters
777
+ ----------
778
+ recommendation_id : str
779
+
780
+ method : str
781
+ Implementation method: 'one-click', 'iac', 'one-click-plus-iac', 'manual'
782
+
783
+ request_options : typing.Optional[RequestOptions]
784
+ Request-specific configuration.
785
+
786
+ Returns
787
+ -------
788
+ HttpResponse[ImplementationMethodResponse]
789
+ Successful Response
790
+ """
791
+ _response = self._client_wrapper.httpx_client.request(
792
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/implementation",
793
+ method="POST",
794
+ json={
795
+ "method": method,
796
+ },
797
+ headers={
798
+ "content-type": "application/json",
799
+ },
800
+ request_options=request_options,
801
+ omit=OMIT,
802
+ )
803
+ try:
804
+ if 200 <= _response.status_code < 300:
805
+ _data = typing.cast(
806
+ ImplementationMethodResponse,
807
+ parse_obj_as(
808
+ type_=ImplementationMethodResponse, # type: ignore
809
+ object_=_response.json(),
810
+ ),
811
+ )
812
+ return HttpResponse(response=_response, data=_data)
813
+ if _response.status_code == 401:
814
+ raise UnauthorizedError(
815
+ headers=dict(_response.headers),
816
+ body=typing.cast(
817
+ AuthenticationError,
818
+ parse_obj_as(
819
+ type_=AuthenticationError, # type: ignore
820
+ object_=_response.json(),
821
+ ),
822
+ ),
823
+ )
824
+ if _response.status_code == 403:
825
+ raise ForbiddenError(
826
+ headers=dict(_response.headers),
827
+ body=typing.cast(
828
+ AuthorizationError,
829
+ parse_obj_as(
830
+ type_=AuthorizationError, # type: ignore
831
+ object_=_response.json(),
832
+ ),
833
+ ),
834
+ )
835
+ if _response.status_code == 422:
836
+ raise UnprocessableEntityError(
837
+ headers=dict(_response.headers),
838
+ body=typing.cast(
839
+ HttpValidationError,
840
+ parse_obj_as(
841
+ type_=HttpValidationError, # type: ignore
842
+ object_=_response.json(),
843
+ ),
844
+ ),
845
+ )
846
+ _response_json = _response.json()
847
+ except JSONDecodeError:
848
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
849
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
850
+
851
+ def complete_wizard(
852
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
853
+ ) -> HttpResponse[CompleteWizardResponse]:
854
+ """
855
+ Completes the wizard flow and triggers the appropriate workflow
856
+
857
+ Parameters
858
+ ----------
859
+ recommendation_id : str
860
+
861
+ request_options : typing.Optional[RequestOptions]
862
+ Request-specific configuration.
863
+
864
+ Returns
865
+ -------
866
+ HttpResponse[CompleteWizardResponse]
867
+ Successful Response
868
+ """
869
+ _response = self._client_wrapper.httpx_client.request(
870
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/complete",
871
+ method="POST",
872
+ request_options=request_options,
873
+ )
874
+ try:
875
+ if 200 <= _response.status_code < 300:
876
+ _data = typing.cast(
877
+ CompleteWizardResponse,
878
+ parse_obj_as(
879
+ type_=CompleteWizardResponse, # type: ignore
880
+ object_=_response.json(),
881
+ ),
882
+ )
883
+ return HttpResponse(response=_response, data=_data)
884
+ if _response.status_code == 401:
885
+ raise UnauthorizedError(
886
+ headers=dict(_response.headers),
887
+ body=typing.cast(
888
+ AuthenticationError,
889
+ parse_obj_as(
890
+ type_=AuthenticationError, # type: ignore
891
+ object_=_response.json(),
892
+ ),
893
+ ),
894
+ )
895
+ if _response.status_code == 403:
896
+ raise ForbiddenError(
897
+ headers=dict(_response.headers),
898
+ body=typing.cast(
899
+ AuthorizationError,
900
+ parse_obj_as(
901
+ type_=AuthorizationError, # type: ignore
902
+ object_=_response.json(),
903
+ ),
904
+ ),
905
+ )
906
+ if _response.status_code == 422:
907
+ raise UnprocessableEntityError(
908
+ headers=dict(_response.headers),
909
+ body=typing.cast(
910
+ HttpValidationError,
911
+ parse_obj_as(
912
+ type_=HttpValidationError, # type: ignore
913
+ object_=_response.json(),
914
+ ),
915
+ ),
916
+ )
917
+ _response_json = _response.json()
918
+ except JSONDecodeError:
919
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
920
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
921
+
922
+
923
+ class AsyncRawRecommendationsClient:
924
+ def __init__(self, *, client_wrapper: AsyncClientWrapper):
925
+ self._client_wrapper = client_wrapper
926
+
927
+ async def get_summary(
928
+ self, *, request_options: typing.Optional[RequestOptions] = None
929
+ ) -> AsyncHttpResponse[ProvidersSummaryResponse]:
930
+ """
931
+ Returns provider-level aggregation for the carousel view (AWS, GCP, Azure, K8s)
932
+
933
+ Parameters
934
+ ----------
935
+ request_options : typing.Optional[RequestOptions]
936
+ Request-specific configuration.
937
+
938
+ Returns
939
+ -------
940
+ AsyncHttpResponse[ProvidersSummaryResponse]
941
+ Successful Response
942
+ """
943
+ _response = await self._client_wrapper.httpx_client.request(
944
+ "api/v1/recommendations/summary",
945
+ method="GET",
946
+ request_options=request_options,
947
+ )
948
+ try:
949
+ if 200 <= _response.status_code < 300:
950
+ _data = typing.cast(
951
+ ProvidersSummaryResponse,
952
+ parse_obj_as(
953
+ type_=ProvidersSummaryResponse, # type: ignore
954
+ object_=_response.json(),
955
+ ),
956
+ )
957
+ return AsyncHttpResponse(response=_response, data=_data)
958
+ if _response.status_code == 401:
959
+ raise UnauthorizedError(
960
+ headers=dict(_response.headers),
961
+ body=typing.cast(
962
+ AuthenticationError,
963
+ parse_obj_as(
964
+ type_=AuthenticationError, # type: ignore
965
+ object_=_response.json(),
966
+ ),
967
+ ),
968
+ )
969
+ if _response.status_code == 403:
970
+ raise ForbiddenError(
971
+ headers=dict(_response.headers),
972
+ body=typing.cast(
973
+ AuthorizationError,
974
+ parse_obj_as(
975
+ type_=AuthorizationError, # type: ignore
976
+ object_=_response.json(),
977
+ ),
978
+ ),
979
+ )
980
+ _response_json = _response.json()
981
+ except JSONDecodeError:
982
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
983
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
984
+
985
+ async def get_potential_savings_summary(
986
+ self, *, request_options: typing.Optional[RequestOptions] = None
987
+ ) -> AsyncHttpResponse[ProvidersSummaryResponse]:
988
+ """
989
+ Returns potential savings summary grouped by provider (from dashboard_potential_savings)
990
+
991
+ Parameters
992
+ ----------
993
+ request_options : typing.Optional[RequestOptions]
994
+ Request-specific configuration.
995
+
996
+ Returns
997
+ -------
998
+ AsyncHttpResponse[ProvidersSummaryResponse]
999
+ Successful Response
1000
+ """
1001
+ _response = await self._client_wrapper.httpx_client.request(
1002
+ "api/v1/recommendations/potential-savings/summary",
1003
+ method="GET",
1004
+ request_options=request_options,
1005
+ )
1006
+ try:
1007
+ if 200 <= _response.status_code < 300:
1008
+ _data = typing.cast(
1009
+ ProvidersSummaryResponse,
1010
+ parse_obj_as(
1011
+ type_=ProvidersSummaryResponse, # type: ignore
1012
+ object_=_response.json(),
1013
+ ),
1014
+ )
1015
+ return AsyncHttpResponse(response=_response, data=_data)
1016
+ if _response.status_code == 401:
1017
+ raise UnauthorizedError(
1018
+ headers=dict(_response.headers),
1019
+ body=typing.cast(
1020
+ AuthenticationError,
1021
+ parse_obj_as(
1022
+ type_=AuthenticationError, # type: ignore
1023
+ object_=_response.json(),
1024
+ ),
1025
+ ),
1026
+ )
1027
+ if _response.status_code == 403:
1028
+ raise ForbiddenError(
1029
+ headers=dict(_response.headers),
1030
+ body=typing.cast(
1031
+ AuthorizationError,
1032
+ parse_obj_as(
1033
+ type_=AuthorizationError, # type: ignore
1034
+ object_=_response.json(),
1035
+ ),
1036
+ ),
1037
+ )
1038
+ _response_json = _response.json()
1039
+ except JSONDecodeError:
1040
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1041
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1042
+
1043
+ async def list(
1044
+ self,
1045
+ *,
1046
+ page: typing.Optional[int] = None,
1047
+ page_size: typing.Optional[int] = None,
1048
+ sort_by: typing.Optional[str] = None,
1049
+ sort_order: typing.Optional[str] = None,
1050
+ request_options: typing.Optional[RequestOptions] = None,
1051
+ ) -> AsyncPager[RecommendationItem, RecommendationsBreakdownResponse]:
1052
+ """
1053
+ Returns paginated list of all recommendations with actions
1054
+
1055
+ Parameters
1056
+ ----------
1057
+ page : typing.Optional[int]
1058
+ Page number (1-indexed)
1059
+
1060
+ page_size : typing.Optional[int]
1061
+ Items per page (max 100)
1062
+
1063
+ sort_by : typing.Optional[str]
1064
+ Field to sort by (recommendation_id, service, environment, monthly_savings, annual_savings, savings_percentage, analysis_period, created_at)
1065
+
1066
+ sort_order : typing.Optional[str]
1067
+ Sort order (asc or desc)
1068
+
1069
+ request_options : typing.Optional[RequestOptions]
1070
+ Request-specific configuration.
1071
+
1072
+ Returns
1073
+ -------
1074
+ AsyncPager[RecommendationItem, RecommendationsBreakdownResponse]
1075
+ Successful Response
1076
+ """
1077
+ page = page if page is not None else 1
1078
+
1079
+ _response = await self._client_wrapper.httpx_client.request(
1080
+ "api/v1/recommendations",
1081
+ method="GET",
1082
+ params={
1083
+ "page": page,
1084
+ "page_size": page_size,
1085
+ "sort_by": sort_by,
1086
+ "sort_order": sort_order,
1087
+ },
1088
+ request_options=request_options,
1089
+ )
1090
+ try:
1091
+ if 200 <= _response.status_code < 300:
1092
+ _parsed_response = typing.cast(
1093
+ RecommendationsBreakdownResponse,
1094
+ parse_obj_as(
1095
+ type_=RecommendationsBreakdownResponse, # type: ignore
1096
+ object_=_response.json(),
1097
+ ),
1098
+ )
1099
+ _items = _parsed_response.data.items if _parsed_response.data is not None else []
1100
+
1101
+ _has_next = True
1102
+
1103
+ async def _get_next():
1104
+ return await self.list(
1105
+ page=page + 1,
1106
+ page_size=page_size,
1107
+ sort_by=sort_by,
1108
+ sort_order=sort_order,
1109
+ request_options=request_options,
1110
+ )
1111
+
1112
+ return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response)
1113
+ if _response.status_code == 401:
1114
+ raise UnauthorizedError(
1115
+ headers=dict(_response.headers),
1116
+ body=typing.cast(
1117
+ AuthenticationError,
1118
+ parse_obj_as(
1119
+ type_=AuthenticationError, # type: ignore
1120
+ object_=_response.json(),
1121
+ ),
1122
+ ),
1123
+ )
1124
+ if _response.status_code == 403:
1125
+ raise ForbiddenError(
1126
+ headers=dict(_response.headers),
1127
+ body=typing.cast(
1128
+ AuthorizationError,
1129
+ parse_obj_as(
1130
+ type_=AuthorizationError, # type: ignore
1131
+ object_=_response.json(),
1132
+ ),
1133
+ ),
1134
+ )
1135
+ if _response.status_code == 422:
1136
+ raise UnprocessableEntityError(
1137
+ headers=dict(_response.headers),
1138
+ body=typing.cast(
1139
+ HttpValidationError,
1140
+ parse_obj_as(
1141
+ type_=HttpValidationError, # type: ignore
1142
+ object_=_response.json(),
1143
+ ),
1144
+ ),
1145
+ )
1146
+ _response_json = _response.json()
1147
+ except JSONDecodeError:
1148
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1149
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1150
+
1151
+ async def get_overview(
1152
+ self, *, request_options: typing.Optional[RequestOptions] = None
1153
+ ) -> AsyncHttpResponse[RecommendationsOverviewResponse]:
1154
+ """
1155
+ Returns recommendations overview metrics across all providers (for dashboard)
1156
+
1157
+ Parameters
1158
+ ----------
1159
+ request_options : typing.Optional[RequestOptions]
1160
+ Request-specific configuration.
1161
+
1162
+ Returns
1163
+ -------
1164
+ AsyncHttpResponse[RecommendationsOverviewResponse]
1165
+ Successful Response
1166
+ """
1167
+ _response = await self._client_wrapper.httpx_client.request(
1168
+ "api/v1/recommendations/overview",
1169
+ method="GET",
1170
+ request_options=request_options,
1171
+ )
1172
+ try:
1173
+ if 200 <= _response.status_code < 300:
1174
+ _data = typing.cast(
1175
+ RecommendationsOverviewResponse,
1176
+ parse_obj_as(
1177
+ type_=RecommendationsOverviewResponse, # type: ignore
1178
+ object_=_response.json(),
1179
+ ),
1180
+ )
1181
+ return AsyncHttpResponse(response=_response, data=_data)
1182
+ if _response.status_code == 401:
1183
+ raise UnauthorizedError(
1184
+ headers=dict(_response.headers),
1185
+ body=typing.cast(
1186
+ AuthenticationError,
1187
+ parse_obj_as(
1188
+ type_=AuthenticationError, # type: ignore
1189
+ object_=_response.json(),
1190
+ ),
1191
+ ),
1192
+ )
1193
+ if _response.status_code == 403:
1194
+ raise ForbiddenError(
1195
+ headers=dict(_response.headers),
1196
+ body=typing.cast(
1197
+ AuthorizationError,
1198
+ parse_obj_as(
1199
+ type_=AuthorizationError, # type: ignore
1200
+ object_=_response.json(),
1201
+ ),
1202
+ ),
1203
+ )
1204
+ _response_json = _response.json()
1205
+ except JSONDecodeError:
1206
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1207
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1208
+
1209
+ async def get_processing(
1210
+ self, *, request_options: typing.Optional[RequestOptions] = None
1211
+ ) -> AsyncHttpResponse[ProcessingRecommendationsResponse]:
1212
+ """
1213
+ Returns all recommendations currently in processing status
1214
+
1215
+ Parameters
1216
+ ----------
1217
+ request_options : typing.Optional[RequestOptions]
1218
+ Request-specific configuration.
1219
+
1220
+ Returns
1221
+ -------
1222
+ AsyncHttpResponse[ProcessingRecommendationsResponse]
1223
+ Successful Response
1224
+ """
1225
+ _response = await self._client_wrapper.httpx_client.request(
1226
+ "api/v1/recommendations/processing",
1227
+ method="GET",
1228
+ request_options=request_options,
1229
+ )
1230
+ try:
1231
+ if 200 <= _response.status_code < 300:
1232
+ _data = typing.cast(
1233
+ ProcessingRecommendationsResponse,
1234
+ parse_obj_as(
1235
+ type_=ProcessingRecommendationsResponse, # type: ignore
1236
+ object_=_response.json(),
1237
+ ),
1238
+ )
1239
+ return AsyncHttpResponse(response=_response, data=_data)
1240
+ if _response.status_code == 401:
1241
+ raise UnauthorizedError(
1242
+ headers=dict(_response.headers),
1243
+ body=typing.cast(
1244
+ AuthenticationError,
1245
+ parse_obj_as(
1246
+ type_=AuthenticationError, # type: ignore
1247
+ object_=_response.json(),
1248
+ ),
1249
+ ),
1250
+ )
1251
+ if _response.status_code == 403:
1252
+ raise ForbiddenError(
1253
+ headers=dict(_response.headers),
1254
+ body=typing.cast(
1255
+ AuthorizationError,
1256
+ parse_obj_as(
1257
+ type_=AuthorizationError, # type: ignore
1258
+ object_=_response.json(),
1259
+ ),
1260
+ ),
1261
+ )
1262
+ _response_json = _response.json()
1263
+ except JSONDecodeError:
1264
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1265
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1266
+
1267
+ async def get_details(
1268
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
1269
+ ) -> AsyncHttpResponse[RecommendationDetailResponse]:
1270
+ """
1271
+ Returns complete details for a specific recommendation
1272
+
1273
+ Parameters
1274
+ ----------
1275
+ recommendation_id : str
1276
+
1277
+ request_options : typing.Optional[RequestOptions]
1278
+ Request-specific configuration.
1279
+
1280
+ Returns
1281
+ -------
1282
+ AsyncHttpResponse[RecommendationDetailResponse]
1283
+ Successful Response
1284
+ """
1285
+ _response = await self._client_wrapper.httpx_client.request(
1286
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/details",
1287
+ method="GET",
1288
+ request_options=request_options,
1289
+ )
1290
+ try:
1291
+ if 200 <= _response.status_code < 300:
1292
+ _data = typing.cast(
1293
+ RecommendationDetailResponse,
1294
+ parse_obj_as(
1295
+ type_=RecommendationDetailResponse, # type: ignore
1296
+ object_=_response.json(),
1297
+ ),
1298
+ )
1299
+ return AsyncHttpResponse(response=_response, data=_data)
1300
+ if _response.status_code == 401:
1301
+ raise UnauthorizedError(
1302
+ headers=dict(_response.headers),
1303
+ body=typing.cast(
1304
+ AuthenticationError,
1305
+ parse_obj_as(
1306
+ type_=AuthenticationError, # type: ignore
1307
+ object_=_response.json(),
1308
+ ),
1309
+ ),
1310
+ )
1311
+ if _response.status_code == 403:
1312
+ raise ForbiddenError(
1313
+ headers=dict(_response.headers),
1314
+ body=typing.cast(
1315
+ AuthorizationError,
1316
+ parse_obj_as(
1317
+ type_=AuthorizationError, # type: ignore
1318
+ object_=_response.json(),
1319
+ ),
1320
+ ),
1321
+ )
1322
+ if _response.status_code == 422:
1323
+ raise UnprocessableEntityError(
1324
+ headers=dict(_response.headers),
1325
+ body=typing.cast(
1326
+ HttpValidationError,
1327
+ parse_obj_as(
1328
+ type_=HttpValidationError, # type: ignore
1329
+ object_=_response.json(),
1330
+ ),
1331
+ ),
1332
+ )
1333
+ _response_json = _response.json()
1334
+ except JSONDecodeError:
1335
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1336
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1337
+
1338
+ async def get_wizard(
1339
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
1340
+ ) -> AsyncHttpResponse[RecommendationWizardResponse]:
1341
+ """
1342
+ Returns recommendation details for the Save Now wizard flow
1343
+
1344
+ Parameters
1345
+ ----------
1346
+ recommendation_id : str
1347
+
1348
+ request_options : typing.Optional[RequestOptions]
1349
+ Request-specific configuration.
1350
+
1351
+ Returns
1352
+ -------
1353
+ AsyncHttpResponse[RecommendationWizardResponse]
1354
+ Successful Response
1355
+ """
1356
+ _response = await self._client_wrapper.httpx_client.request(
1357
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}",
1358
+ method="GET",
1359
+ request_options=request_options,
1360
+ )
1361
+ try:
1362
+ if 200 <= _response.status_code < 300:
1363
+ _data = typing.cast(
1364
+ RecommendationWizardResponse,
1365
+ parse_obj_as(
1366
+ type_=RecommendationWizardResponse, # type: ignore
1367
+ object_=_response.json(),
1368
+ ),
1369
+ )
1370
+ return AsyncHttpResponse(response=_response, data=_data)
1371
+ if _response.status_code == 401:
1372
+ raise UnauthorizedError(
1373
+ headers=dict(_response.headers),
1374
+ body=typing.cast(
1375
+ AuthenticationError,
1376
+ parse_obj_as(
1377
+ type_=AuthenticationError, # type: ignore
1378
+ object_=_response.json(),
1379
+ ),
1380
+ ),
1381
+ )
1382
+ if _response.status_code == 403:
1383
+ raise ForbiddenError(
1384
+ headers=dict(_response.headers),
1385
+ body=typing.cast(
1386
+ AuthorizationError,
1387
+ parse_obj_as(
1388
+ type_=AuthorizationError, # type: ignore
1389
+ object_=_response.json(),
1390
+ ),
1391
+ ),
1392
+ )
1393
+ if _response.status_code == 422:
1394
+ raise UnprocessableEntityError(
1395
+ headers=dict(_response.headers),
1396
+ body=typing.cast(
1397
+ HttpValidationError,
1398
+ parse_obj_as(
1399
+ type_=HttpValidationError, # type: ignore
1400
+ object_=_response.json(),
1401
+ ),
1402
+ ),
1403
+ )
1404
+ _response_json = _response.json()
1405
+ except JSONDecodeError:
1406
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1407
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1408
+
1409
+ async def accept_terms(
1410
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
1411
+ ) -> AsyncHttpResponse[AcceptTermsResponse]:
1412
+ """
1413
+ Records user acceptance of terms and conditions for a recommendation
1414
+
1415
+ Parameters
1416
+ ----------
1417
+ recommendation_id : str
1418
+
1419
+ request_options : typing.Optional[RequestOptions]
1420
+ Request-specific configuration.
1421
+
1422
+ Returns
1423
+ -------
1424
+ AsyncHttpResponse[AcceptTermsResponse]
1425
+ Successful Response
1426
+ """
1427
+ _response = await self._client_wrapper.httpx_client.request(
1428
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/accept-terms",
1429
+ method="POST",
1430
+ request_options=request_options,
1431
+ )
1432
+ try:
1433
+ if 200 <= _response.status_code < 300:
1434
+ _data = typing.cast(
1435
+ AcceptTermsResponse,
1436
+ parse_obj_as(
1437
+ type_=AcceptTermsResponse, # type: ignore
1438
+ object_=_response.json(),
1439
+ ),
1440
+ )
1441
+ return AsyncHttpResponse(response=_response, data=_data)
1442
+ if _response.status_code == 401:
1443
+ raise UnauthorizedError(
1444
+ headers=dict(_response.headers),
1445
+ body=typing.cast(
1446
+ AuthenticationError,
1447
+ parse_obj_as(
1448
+ type_=AuthenticationError, # type: ignore
1449
+ object_=_response.json(),
1450
+ ),
1451
+ ),
1452
+ )
1453
+ if _response.status_code == 403:
1454
+ raise ForbiddenError(
1455
+ headers=dict(_response.headers),
1456
+ body=typing.cast(
1457
+ AuthorizationError,
1458
+ parse_obj_as(
1459
+ type_=AuthorizationError, # type: ignore
1460
+ object_=_response.json(),
1461
+ ),
1462
+ ),
1463
+ )
1464
+ if _response.status_code == 422:
1465
+ raise UnprocessableEntityError(
1466
+ headers=dict(_response.headers),
1467
+ body=typing.cast(
1468
+ HttpValidationError,
1469
+ parse_obj_as(
1470
+ type_=HttpValidationError, # type: ignore
1471
+ object_=_response.json(),
1472
+ ),
1473
+ ),
1474
+ )
1475
+ _response_json = _response.json()
1476
+ except JSONDecodeError:
1477
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1478
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1479
+
1480
+ async def update_tag(
1481
+ self,
1482
+ recommendation_id: str,
1483
+ *,
1484
+ tag: typing.Optional[str] = OMIT,
1485
+ request_options: typing.Optional[RequestOptions] = None,
1486
+ ) -> AsyncHttpResponse[UpdateTagResponse]:
1487
+ """
1488
+ Updates the tag value for a recommendation
1489
+
1490
+ Parameters
1491
+ ----------
1492
+ recommendation_id : str
1493
+
1494
+ tag : typing.Optional[str]
1495
+ New tag value. Send null to clear.
1496
+
1497
+ request_options : typing.Optional[RequestOptions]
1498
+ Request-specific configuration.
1499
+
1500
+ Returns
1501
+ -------
1502
+ AsyncHttpResponse[UpdateTagResponse]
1503
+ Successful Response
1504
+ """
1505
+ _response = await self._client_wrapper.httpx_client.request(
1506
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/tag",
1507
+ method="POST",
1508
+ json={
1509
+ "tag": tag,
1510
+ },
1511
+ headers={
1512
+ "content-type": "application/json",
1513
+ },
1514
+ request_options=request_options,
1515
+ omit=OMIT,
1516
+ )
1517
+ try:
1518
+ if 200 <= _response.status_code < 300:
1519
+ _data = typing.cast(
1520
+ UpdateTagResponse,
1521
+ parse_obj_as(
1522
+ type_=UpdateTagResponse, # type: ignore
1523
+ object_=_response.json(),
1524
+ ),
1525
+ )
1526
+ return AsyncHttpResponse(response=_response, data=_data)
1527
+ if _response.status_code == 401:
1528
+ raise UnauthorizedError(
1529
+ headers=dict(_response.headers),
1530
+ body=typing.cast(
1531
+ AuthenticationError,
1532
+ parse_obj_as(
1533
+ type_=AuthenticationError, # type: ignore
1534
+ object_=_response.json(),
1535
+ ),
1536
+ ),
1537
+ )
1538
+ if _response.status_code == 403:
1539
+ raise ForbiddenError(
1540
+ headers=dict(_response.headers),
1541
+ body=typing.cast(
1542
+ AuthorizationError,
1543
+ parse_obj_as(
1544
+ type_=AuthorizationError, # type: ignore
1545
+ object_=_response.json(),
1546
+ ),
1547
+ ),
1548
+ )
1549
+ if _response.status_code == 422:
1550
+ raise UnprocessableEntityError(
1551
+ headers=dict(_response.headers),
1552
+ body=typing.cast(
1553
+ HttpValidationError,
1554
+ parse_obj_as(
1555
+ type_=HttpValidationError, # type: ignore
1556
+ object_=_response.json(),
1557
+ ),
1558
+ ),
1559
+ )
1560
+ _response_json = _response.json()
1561
+ except JSONDecodeError:
1562
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1563
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1564
+
1565
+ async def make_decision(
1566
+ self,
1567
+ recommendation_id: str,
1568
+ *,
1569
+ decision: str,
1570
+ reason: typing.Optional[str] = OMIT,
1571
+ explanation: typing.Optional[str] = OMIT,
1572
+ request_options: typing.Optional[RequestOptions] = None,
1573
+ ) -> AsyncHttpResponse[SavingsDecisionResponse]:
1574
+ """
1575
+ Records user decision to accept or reject a savings recommendation
1576
+
1577
+ Parameters
1578
+ ----------
1579
+ recommendation_id : str
1580
+
1581
+ decision : str
1582
+ Decision: 'accepted' or 'rejected'
1583
+
1584
+ reason : typing.Optional[str]
1585
+ Rejection reason: operational, strategy, not_applicable, other (required if rejected)
1586
+
1587
+ explanation : typing.Optional[str]
1588
+ Free text explanation (optional, used when reason is 'other')
1589
+
1590
+ request_options : typing.Optional[RequestOptions]
1591
+ Request-specific configuration.
1592
+
1593
+ Returns
1594
+ -------
1595
+ AsyncHttpResponse[SavingsDecisionResponse]
1596
+ Successful Response
1597
+ """
1598
+ _response = await self._client_wrapper.httpx_client.request(
1599
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/decision",
1600
+ method="POST",
1601
+ json={
1602
+ "decision": decision,
1603
+ "reason": reason,
1604
+ "explanation": explanation,
1605
+ },
1606
+ headers={
1607
+ "content-type": "application/json",
1608
+ },
1609
+ request_options=request_options,
1610
+ omit=OMIT,
1611
+ )
1612
+ try:
1613
+ if 200 <= _response.status_code < 300:
1614
+ _data = typing.cast(
1615
+ SavingsDecisionResponse,
1616
+ parse_obj_as(
1617
+ type_=SavingsDecisionResponse, # type: ignore
1618
+ object_=_response.json(),
1619
+ ),
1620
+ )
1621
+ return AsyncHttpResponse(response=_response, data=_data)
1622
+ if _response.status_code == 401:
1623
+ raise UnauthorizedError(
1624
+ headers=dict(_response.headers),
1625
+ body=typing.cast(
1626
+ AuthenticationError,
1627
+ parse_obj_as(
1628
+ type_=AuthenticationError, # type: ignore
1629
+ object_=_response.json(),
1630
+ ),
1631
+ ),
1632
+ )
1633
+ if _response.status_code == 403:
1634
+ raise ForbiddenError(
1635
+ headers=dict(_response.headers),
1636
+ body=typing.cast(
1637
+ AuthorizationError,
1638
+ parse_obj_as(
1639
+ type_=AuthorizationError, # type: ignore
1640
+ object_=_response.json(),
1641
+ ),
1642
+ ),
1643
+ )
1644
+ if _response.status_code == 422:
1645
+ raise UnprocessableEntityError(
1646
+ headers=dict(_response.headers),
1647
+ body=typing.cast(
1648
+ HttpValidationError,
1649
+ parse_obj_as(
1650
+ type_=HttpValidationError, # type: ignore
1651
+ object_=_response.json(),
1652
+ ),
1653
+ ),
1654
+ )
1655
+ _response_json = _response.json()
1656
+ except JSONDecodeError:
1657
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1658
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1659
+
1660
+ async def set_implementation(
1661
+ self, recommendation_id: str, *, method: str, request_options: typing.Optional[RequestOptions] = None
1662
+ ) -> AsyncHttpResponse[ImplementationMethodResponse]:
1663
+ """
1664
+ Records the chosen implementation method for a recommendation
1665
+
1666
+ Parameters
1667
+ ----------
1668
+ recommendation_id : str
1669
+
1670
+ method : str
1671
+ Implementation method: 'one-click', 'iac', 'one-click-plus-iac', 'manual'
1672
+
1673
+ request_options : typing.Optional[RequestOptions]
1674
+ Request-specific configuration.
1675
+
1676
+ Returns
1677
+ -------
1678
+ AsyncHttpResponse[ImplementationMethodResponse]
1679
+ Successful Response
1680
+ """
1681
+ _response = await self._client_wrapper.httpx_client.request(
1682
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/implementation",
1683
+ method="POST",
1684
+ json={
1685
+ "method": method,
1686
+ },
1687
+ headers={
1688
+ "content-type": "application/json",
1689
+ },
1690
+ request_options=request_options,
1691
+ omit=OMIT,
1692
+ )
1693
+ try:
1694
+ if 200 <= _response.status_code < 300:
1695
+ _data = typing.cast(
1696
+ ImplementationMethodResponse,
1697
+ parse_obj_as(
1698
+ type_=ImplementationMethodResponse, # type: ignore
1699
+ object_=_response.json(),
1700
+ ),
1701
+ )
1702
+ return AsyncHttpResponse(response=_response, data=_data)
1703
+ if _response.status_code == 401:
1704
+ raise UnauthorizedError(
1705
+ headers=dict(_response.headers),
1706
+ body=typing.cast(
1707
+ AuthenticationError,
1708
+ parse_obj_as(
1709
+ type_=AuthenticationError, # type: ignore
1710
+ object_=_response.json(),
1711
+ ),
1712
+ ),
1713
+ )
1714
+ if _response.status_code == 403:
1715
+ raise ForbiddenError(
1716
+ headers=dict(_response.headers),
1717
+ body=typing.cast(
1718
+ AuthorizationError,
1719
+ parse_obj_as(
1720
+ type_=AuthorizationError, # type: ignore
1721
+ object_=_response.json(),
1722
+ ),
1723
+ ),
1724
+ )
1725
+ if _response.status_code == 422:
1726
+ raise UnprocessableEntityError(
1727
+ headers=dict(_response.headers),
1728
+ body=typing.cast(
1729
+ HttpValidationError,
1730
+ parse_obj_as(
1731
+ type_=HttpValidationError, # type: ignore
1732
+ object_=_response.json(),
1733
+ ),
1734
+ ),
1735
+ )
1736
+ _response_json = _response.json()
1737
+ except JSONDecodeError:
1738
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1739
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)
1740
+
1741
+ async def complete_wizard(
1742
+ self, recommendation_id: str, *, request_options: typing.Optional[RequestOptions] = None
1743
+ ) -> AsyncHttpResponse[CompleteWizardResponse]:
1744
+ """
1745
+ Completes the wizard flow and triggers the appropriate workflow
1746
+
1747
+ Parameters
1748
+ ----------
1749
+ recommendation_id : str
1750
+
1751
+ request_options : typing.Optional[RequestOptions]
1752
+ Request-specific configuration.
1753
+
1754
+ Returns
1755
+ -------
1756
+ AsyncHttpResponse[CompleteWizardResponse]
1757
+ Successful Response
1758
+ """
1759
+ _response = await self._client_wrapper.httpx_client.request(
1760
+ f"api/v1/recommendations/{jsonable_encoder(recommendation_id)}/complete",
1761
+ method="POST",
1762
+ request_options=request_options,
1763
+ )
1764
+ try:
1765
+ if 200 <= _response.status_code < 300:
1766
+ _data = typing.cast(
1767
+ CompleteWizardResponse,
1768
+ parse_obj_as(
1769
+ type_=CompleteWizardResponse, # type: ignore
1770
+ object_=_response.json(),
1771
+ ),
1772
+ )
1773
+ return AsyncHttpResponse(response=_response, data=_data)
1774
+ if _response.status_code == 401:
1775
+ raise UnauthorizedError(
1776
+ headers=dict(_response.headers),
1777
+ body=typing.cast(
1778
+ AuthenticationError,
1779
+ parse_obj_as(
1780
+ type_=AuthenticationError, # type: ignore
1781
+ object_=_response.json(),
1782
+ ),
1783
+ ),
1784
+ )
1785
+ if _response.status_code == 403:
1786
+ raise ForbiddenError(
1787
+ headers=dict(_response.headers),
1788
+ body=typing.cast(
1789
+ AuthorizationError,
1790
+ parse_obj_as(
1791
+ type_=AuthorizationError, # type: ignore
1792
+ object_=_response.json(),
1793
+ ),
1794
+ ),
1795
+ )
1796
+ if _response.status_code == 422:
1797
+ raise UnprocessableEntityError(
1798
+ headers=dict(_response.headers),
1799
+ body=typing.cast(
1800
+ HttpValidationError,
1801
+ parse_obj_as(
1802
+ type_=HttpValidationError, # type: ignore
1803
+ object_=_response.json(),
1804
+ ),
1805
+ ),
1806
+ )
1807
+ _response_json = _response.json()
1808
+ except JSONDecodeError:
1809
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text)
1810
+ raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json)