qcs_api_client_openapi/apis/
engagements_api.rs1use super::{ContentType, Error, configuration};
26use crate::{apis::ResponseContent, models};
27use ::qcs_api_client_common::backoff::{
28 BackoffBuilder, ExponentialBackoff, duration_from_io_error, duration_from_reqwest_error,
29 duration_from_response,
30};
31#[cfg(feature = "tracing")]
32use qcs_api_client_common::configuration::tokens::TokenRefresher;
33use qcs_dependencies_client::reqwest::{self, StatusCode};
34use serde::{Deserialize, Serialize};
35
36#[cfg(feature = "clap")]
37#[allow(unused, reason = "not used in all templates, but required in some")]
38use ::{miette::IntoDiagnostic as _, qcs_api_client_common::clap_utils::JsonMaybeStdin};
39
40#[cfg(feature = "clap")]
42#[derive(Debug, clap::Args)]
43pub struct CreateEngagementClapParams {
44 pub create_engagement_request: JsonMaybeStdin<crate::models::CreateEngagementRequest>,
45 #[arg(long)]
47 pub x_qcs_account_id: Option<String>,
48 #[arg(long)]
50 pub x_qcs_account_type: Option<models::AccountType>,
51}
52
53#[cfg(feature = "clap")]
54impl CreateEngagementClapParams {
55 pub async fn execute(
56 self,
57 configuration: &configuration::Configuration,
58 ) -> Result<models::EngagementWithCredentials, miette::Error> {
59 let request = self.create_engagement_request.into_inner().into_inner();
60
61 create_engagement(
62 configuration,
63 request,
64 self.x_qcs_account_id.as_deref(),
65 self.x_qcs_account_type,
66 )
67 .await
68 .into_diagnostic()
69 }
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(untagged)]
75pub enum CreateEngagementError {
76 Status400(models::Error),
77 Status404(models::Error),
78 Status422(models::Error),
79 Status503(),
80 UnknownValue(serde_json::Value),
81}
82
83async fn create_engagement_inner(
84 configuration: &configuration::Configuration,
85 backoff: &mut ExponentialBackoff,
86 create_engagement_request: crate::models::CreateEngagementRequest,
87 x_qcs_account_id: Option<&str>,
88 x_qcs_account_type: Option<models::AccountType>,
89) -> Result<models::EngagementWithCredentials, Error<CreateEngagementError>> {
90 let local_var_configuration = configuration;
91 let p_body_create_engagement_request = create_engagement_request;
93 let p_header_x_qcs_account_id = x_qcs_account_id;
94 let p_header_x_qcs_account_type = x_qcs_account_type;
95
96 let local_var_client = &local_var_configuration.client;
97
98 let local_var_uri_str = format!(
99 "{}/v1/engagements",
100 local_var_configuration.qcs_config.api_url()
101 );
102 let mut local_var_req_builder =
103 local_var_client.request(reqwest::Method::POST, local_var_uri_str.as_str());
104
105 #[cfg(feature = "tracing")]
106 {
107 let local_var_do_tracing = local_var_uri_str
110 .parse::<::url::Url>()
111 .ok()
112 .is_none_or(|url| {
113 configuration
114 .qcs_config
115 .should_trace(&::urlpattern::UrlPatternMatchInput::Url(url))
116 });
117
118 if local_var_do_tracing {
119 ::tracing::debug!(
120 url=%local_var_uri_str,
121 method="POST",
122 "making create_engagement request",
123 );
124 }
125 }
126
127 if let Some(local_var_param_value) = p_header_x_qcs_account_id {
128 local_var_req_builder =
129 local_var_req_builder.header("x-qcs-account-id", local_var_param_value.to_string());
130 }
131 if let Some(local_var_param_value) = p_header_x_qcs_account_type {
132 local_var_req_builder =
133 local_var_req_builder.header("x-qcs-account-type", local_var_param_value.to_string());
134 }
135
136 {
139 use qcs_api_client_common::configuration::TokenError;
140
141 #[allow(
142 clippy::nonminimal_bool,
143 clippy::eq_op,
144 reason = "Logic must be done at runtime since it cannot be handled by the mustache template engine."
145 )]
146 let is_jwt_bearer_optional: bool = false || "JWTBearer" == "JWTBearerOptional";
147
148 let token = local_var_configuration
149 .qcs_config
150 .get_bearer_access_token()
151 .await;
152
153 if is_jwt_bearer_optional && matches!(token, Err(TokenError::NoCredentials)) {
154 #[cfg(feature = "tracing")]
156 tracing::debug!(
157 "No client credentials found, but this call does not require authentication."
158 );
159 } else {
160 local_var_req_builder = local_var_req_builder.bearer_auth(token?.secret());
161 }
162 }
163
164 local_var_req_builder = local_var_req_builder.json(&p_body_create_engagement_request);
165
166 let local_var_req = local_var_req_builder.build()?;
167 let local_var_resp = local_var_client.execute(local_var_req).await?;
168
169 let local_var_status = local_var_resp.status();
170 let local_var_raw_content_type = local_var_resp
171 .headers()
172 .get("content-type")
173 .and_then(|v| v.to_str().ok())
174 .unwrap_or("application/octet-stream")
175 .to_string();
176 let local_var_content_type = super::ContentType::from(local_var_raw_content_type.as_str());
177
178 if !local_var_status.is_client_error() && !local_var_status.is_server_error() {
179 let local_var_content = local_var_resp.text().await?;
180 match local_var_content_type {
181 ContentType::Json => serde_path_to_error::deserialize(
182 &mut serde_json::Deserializer::from_str(&local_var_content),
183 )
184 .map_err(Error::from),
185 ContentType::Text => Err(Error::InvalidContentType {
186 content_type: local_var_raw_content_type,
187 return_type: "models::EngagementWithCredentials",
188 }),
189 ContentType::Unsupported(unknown_type) => Err(Error::InvalidContentType {
190 content_type: unknown_type,
191 return_type: "models::EngagementWithCredentials",
192 }),
193 }
194 } else {
195 let local_var_retry_delay =
196 duration_from_response(local_var_resp.status(), local_var_resp.headers(), backoff);
197 let local_var_content = local_var_resp.text().await?;
198 let local_var_entity: Option<CreateEngagementError> =
199 serde_json::from_str(&local_var_content).ok();
200 let local_var_error = ResponseContent {
201 status: local_var_status,
202 content: local_var_content,
203 entity: local_var_entity,
204 retry_delay: local_var_retry_delay,
205 };
206 Err(Error::ResponseError(local_var_error))
207 }
208}
209
210pub async fn create_engagement(
212 configuration: &configuration::Configuration,
213 create_engagement_request: crate::models::CreateEngagementRequest,
214 x_qcs_account_id: Option<&str>,
215 x_qcs_account_type: Option<models::AccountType>,
216) -> Result<models::EngagementWithCredentials, Error<CreateEngagementError>> {
217 let mut backoff = configuration.backoff.build();
218 let mut refreshed_credentials = false;
219 let method = reqwest::Method::POST;
220 loop {
221 let result = create_engagement_inner(
222 configuration,
223 &mut backoff,
224 create_engagement_request.clone(),
225 x_qcs_account_id.clone(),
226 x_qcs_account_type.clone(),
227 )
228 .await;
229
230 match result {
231 Ok(result) => return Ok(result),
232 Err(Error::ResponseError(response)) => {
233 if !refreshed_credentials
234 && matches!(
235 response.status,
236 StatusCode::FORBIDDEN | StatusCode::UNAUTHORIZED
237 )
238 {
239 match configuration.qcs_config.refresh().await {
241 Ok(_) => {
242 refreshed_credentials = true;
243 continue;
244 }
245 Err(::qcs_api_client_common::configuration::TokenError::Write {
246 error,
247 oauth_session: _,
248 }) => {
249 #[cfg(feature = "tracing")]
252 tracing::warn!(
253 "Token refresh succeeded but failed to persist: {}. Continuing with in-memory token.",
254 error
255 );
256 refreshed_credentials = true;
257 continue;
258 }
259 Err(e) => return Err(e.into()),
260 }
261 } else if let Some(duration) = response.retry_delay {
262 tokio::time::sleep(duration).await;
263 continue;
264 }
265
266 return Err(Error::ResponseError(response));
267 }
268 Err(Error::Reqwest(error)) => {
269 if let Some(duration) = duration_from_reqwest_error(&method, &error, &mut backoff) {
270 tokio::time::sleep(duration).await;
271 continue;
272 }
273
274 return Err(Error::Reqwest(error));
275 }
276 Err(Error::Io(error)) => {
277 if let Some(duration) = duration_from_io_error(&method, &error, &mut backoff) {
278 tokio::time::sleep(duration).await;
279 continue;
280 }
281
282 return Err(Error::Io(error));
283 }
284 Err(error) => return Err(error),
285 }
286 }
287}