openstack_sdk_identity/v4/user/passkey/
register_finish.rs1use derive_builder::Builder;
19use http::{HeaderMap, HeaderName, HeaderValue};
20
21use openstack_sdk_core::api::rest_endpoint_prelude::*;
22
23use serde::Deserialize;
24use serde::Serialize;
25use std::borrow::Cow;
26
27#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
29#[builder(setter(strip_option))]
30pub struct CredProps {
31 #[serde(skip_serializing_if = "Option::is_none")]
38 #[builder(default, setter(into))]
39 pub(crate) rk: Option<Option<bool>>,
40}
41
42#[derive(Debug, Deserialize, Clone, Serialize)]
43pub enum CredProtect {
44 #[serde(rename = "Optional")]
45 Optional,
46 #[serde(rename = "OptionalWithCredentialIDList")]
47 Optionalwithcredentialidlist,
48 #[serde(rename = "Required")]
49 Required,
50}
51
52#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
55#[builder(setter(strip_option))]
56pub struct Extensions {
57 #[serde(skip_serializing_if = "Option::is_none")]
59 #[builder(default, setter(into))]
60 pub(crate) appid: Option<bool>,
61
62 #[serde(skip_serializing_if = "Option::is_none")]
64 #[builder(default, setter(into))]
65 pub(crate) cred_props: Option<CredProps>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 #[builder(default)]
70 pub(crate) cred_protect: Option<CredProtect>,
71
72 #[serde(skip_serializing_if = "Option::is_none")]
74 #[builder(default, setter(into))]
75 pub(crate) hmac_secret: Option<bool>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 #[builder(default, setter(into))]
80 pub(crate) min_pin_length: Option<u32>,
81}
82
83#[derive(Debug, Deserialize, Clone, Serialize)]
84pub enum Transports {
85 #[serde(rename = "Ble")]
86 Ble,
87 #[serde(rename = "Hybrid")]
88 Hybrid,
89 #[serde(rename = "Internal")]
90 Internal,
91 #[serde(rename = "Nfc")]
92 Nfc,
93 #[serde(rename = "Test")]
94 Test,
95 #[serde(rename = "Unknown")]
96 Unknown,
97 #[serde(rename = "Usb")]
98 Usb,
99}
100
101#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
103#[builder(setter(strip_option))]
104pub struct Response<'a> {
105 #[serde()]
107 #[builder(setter(into))]
108 pub(crate) attestation_object: Cow<'a, str>,
109
110 #[serde()]
112 #[builder(setter(into))]
113 pub(crate) client_data_json: Cow<'a, str>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
117 #[builder(default, setter(into))]
118 pub(crate) transports: Option<Vec<Transports>>,
119}
120
121#[derive(Builder, Debug, Clone)]
122#[builder(setter(strip_option))]
123pub struct Request<'a> {
124 #[builder(default, setter(into))]
126 pub(crate) description: Option<Cow<'a, str>>,
127
128 #[builder(setter(into))]
131 pub(crate) extensions: Extensions,
132
133 #[builder(setter(into))]
138 pub(crate) id: Cow<'a, str>,
139
140 #[builder(setter(into))]
145 pub(crate) raw_id: Cow<'a, str>,
146
147 #[builder(setter(into))]
149 pub(crate) response: Response<'a>,
150
151 #[builder(setter(into))]
153 pub(crate) type_: Cow<'a, str>,
154
155 #[builder(default, setter(into))]
157 user_id: Cow<'a, str>,
158
159 #[builder(setter(name = "_headers"), default, private)]
160 _headers: Option<HeaderMap>,
161}
162impl<'a> Request<'a> {
163 pub fn builder() -> RequestBuilder<'a> {
165 RequestBuilder::default()
166 }
167}
168
169impl<'a> RequestBuilder<'a> {
170 pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
172 where
173 K: Into<HeaderName>,
174 V: Into<HeaderValue>,
175 {
176 self._headers
177 .get_or_insert(None)
178 .get_or_insert_with(HeaderMap::new)
179 .insert(header_name.into(), header_value.into());
180 self
181 }
182
183 pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
185 where
186 I: Iterator<Item = T>,
187 T: Into<(Option<HeaderName>, HeaderValue)>,
188 {
189 self._headers
190 .get_or_insert(None)
191 .get_or_insert_with(HeaderMap::new)
192 .extend(iter.map(Into::into));
193 self
194 }
195}
196
197impl RestEndpoint for Request<'_> {
198 fn method(&self) -> http::Method {
199 http::Method::POST
200 }
201
202 fn endpoint(&self) -> Cow<'static, str> {
203 format!(
204 "users/{user_id}/passkeys/register_finish",
205 user_id = self.user_id.as_ref(),
206 )
207 .into()
208 }
209
210 fn parameters(&self) -> QueryParams<'_> {
211 QueryParams::default()
212 }
213
214 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
215 let mut params = JsonBodyParams::default();
216
217 if let Some(val) = &self.description {
218 params.push("description", serde_json::to_value(val)?);
219 }
220 params.push("extensions", serde_json::to_value(&self.extensions)?);
221 params.push("id", serde_json::to_value(&self.id)?);
222 params.push("raw_id", serde_json::to_value(&self.raw_id)?);
223 params.push("response", serde_json::to_value(&self.response)?);
224 params.push("type_", serde_json::to_value(&self.type_)?);
225
226 params.into_body()
227 }
228
229 fn service_type(&self) -> ServiceType {
230 ServiceType::Identity
231 }
232
233 fn response_key(&self) -> Option<Cow<'static, str>> {
234 Some("passkey".into())
235 }
236
237 fn request_headers(&self) -> Option<&HeaderMap> {
239 self._headers.as_ref()
240 }
241
242 fn api_version(&self) -> Option<ApiVersion> {
244 Some(ApiVersion::new(4, 0))
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use http::{HeaderName, HeaderValue};
252 use httpmock::MockServer;
253 #[cfg(feature = "sync")]
254 use openstack_sdk_core::api::Query;
255 use openstack_sdk_core::test::client::FakeOpenStackClient;
256 use openstack_sdk_core::types::ServiceType;
257 use serde_json::json;
258
259 #[test]
260 fn test_service_type() {
261 assert_eq!(
262 Request::builder()
263 .extensions(ExtensionsBuilder::default().build().unwrap())
264 .id("foo")
265 .raw_id("foo")
266 .response(
267 ResponseBuilder::default()
268 .attestation_object("foo")
269 .client_data_json("foo")
270 .build()
271 .unwrap()
272 )
273 .type_("foo")
274 .build()
275 .unwrap()
276 .service_type(),
277 ServiceType::Identity
278 );
279 }
280
281 #[test]
282 fn test_response_key() {
283 assert_eq!(
284 Request::builder()
285 .extensions(ExtensionsBuilder::default().build().unwrap())
286 .id("foo")
287 .raw_id("foo")
288 .response(
289 ResponseBuilder::default()
290 .attestation_object("foo")
291 .client_data_json("foo")
292 .build()
293 .unwrap()
294 )
295 .type_("foo")
296 .build()
297 .unwrap()
298 .response_key()
299 .unwrap(),
300 "passkey"
301 );
302 }
303
304 #[cfg(feature = "sync")]
305 #[test]
306 fn endpoint() {
307 let server = MockServer::start();
308 let client = FakeOpenStackClient::new(server.base_url());
309 let mock = server.mock(|when, then| {
310 when.method(httpmock::Method::POST).path(format!(
311 "/users/{user_id}/passkeys/register_finish",
312 user_id = "user_id",
313 ));
314
315 then.status(200)
316 .header("content-type", "application/json")
317 .json_body(json!({ "passkey": {} }));
318 });
319
320 let endpoint = Request::builder()
321 .user_id("user_id")
322 .extensions(ExtensionsBuilder::default().build().unwrap())
323 .id("foo")
324 .raw_id("foo")
325 .response(
326 ResponseBuilder::default()
327 .attestation_object("foo")
328 .client_data_json("foo")
329 .build()
330 .unwrap(),
331 )
332 .type_("foo")
333 .build()
334 .unwrap();
335 let _: serde_json::Value = endpoint.query(&client).unwrap();
336 mock.assert();
337 }
338
339 #[cfg(feature = "sync")]
340 #[test]
341 fn endpoint_headers() {
342 let server = MockServer::start();
343 let client = FakeOpenStackClient::new(server.base_url());
344 let mock = server.mock(|when, then| {
345 when.method(httpmock::Method::POST)
346 .path(format!(
347 "/users/{user_id}/passkeys/register_finish",
348 user_id = "user_id",
349 ))
350 .header("foo", "bar")
351 .header("not_foo", "not_bar");
352 then.status(200)
353 .header("content-type", "application/json")
354 .json_body(json!({ "passkey": {} }));
355 });
356
357 let endpoint = Request::builder()
358 .user_id("user_id")
359 .extensions(ExtensionsBuilder::default().build().unwrap())
360 .id("foo")
361 .raw_id("foo")
362 .response(
363 ResponseBuilder::default()
364 .attestation_object("foo")
365 .client_data_json("foo")
366 .build()
367 .unwrap(),
368 )
369 .type_("foo")
370 .headers(
371 [(
372 Some(HeaderName::from_static("foo")),
373 HeaderValue::from_static("bar"),
374 )]
375 .into_iter(),
376 )
377 .header(
378 HeaderName::from_static("not_foo"),
379 HeaderValue::from_static("not_bar"),
380 )
381 .build()
382 .unwrap();
383 let _: serde_json::Value = endpoint.query(&client).unwrap();
384 mock.assert();
385 }
386}