1use derive_builder::Builder;
23use http::{HeaderMap, HeaderName, HeaderValue};
24
25use openstack_sdk_core::api::rest_endpoint_prelude::*;
26
27use serde::Deserialize;
28use serde::Serialize;
29use std::borrow::Cow;
30
31#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
32#[builder(setter(strip_option))]
33pub struct Domain<'a> {
34 #[serde(skip_serializing_if = "Option::is_none")]
35 #[builder(default, setter(into))]
36 pub(crate) id: Option<Cow<'a, str>>,
37
38 #[serde(skip_serializing_if = "Option::is_none")]
39 #[builder(default, setter(into))]
40 pub(crate) name: Option<Cow<'a, str>>,
41}
42
43#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
44#[builder(setter(strip_option))]
45pub struct Group<'a> {
46 #[serde()]
47 #[builder(setter(into))]
48 pub(crate) id: Cow<'a, str>,
49}
50
51#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
52#[builder(setter(strip_option))]
53pub struct GroupStruct<'a> {
54 #[serde()]
55 #[builder(setter(into))]
56 pub(crate) domain: Domain<'a>,
57
58 #[serde()]
59 #[builder(setter(into))]
60 pub(crate) name: Cow<'a, str>,
61}
62
63#[derive(Debug, Deserialize, Clone, Serialize)]
64#[serde(untagged)]
65pub enum LocalGroup<'a> {
66 F1(Group<'a>),
67 F2(GroupStruct<'a>),
68}
69
70#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
71#[builder(setter(strip_option))]
72pub struct Roles<'a> {
73 #[serde()]
74 #[builder(setter(into))]
75 pub(crate) name: Cow<'a, str>,
76}
77
78#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
79#[builder(setter(strip_option))]
80pub struct Projects<'a> {
81 #[serde(skip_serializing_if = "Option::is_none")]
82 #[builder(default, setter(into))]
83 pub(crate) domain: Option<Domain<'a>>,
84
85 #[serde()]
86 #[builder(setter(into))]
87 pub(crate) name: Cow<'a, str>,
88
89 #[serde()]
90 #[builder(setter(into))]
91 pub(crate) roles: Vec<Roles<'a>>,
92}
93
94#[derive(Debug, Deserialize, Clone, Serialize)]
95pub enum Type {
96 #[serde(rename = "ephemeral")]
97 Ephemeral,
98 #[serde(rename = "local")]
99 Local,
100}
101
102#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
103#[builder(setter(strip_option))]
104pub struct User<'a> {
105 #[serde(skip_serializing_if = "Option::is_none")]
106 #[builder(default, setter(into))]
107 pub(crate) domain: Option<Domain<'a>>,
108
109 #[serde(skip_serializing_if = "Option::is_none")]
110 #[builder(default, setter(into))]
111 pub(crate) email: Option<Cow<'a, str>>,
112
113 #[serde(skip_serializing_if = "Option::is_none")]
114 #[builder(default, setter(into))]
115 pub(crate) id: Option<Cow<'a, str>>,
116
117 #[serde(skip_serializing_if = "Option::is_none")]
118 #[builder(default, setter(into))]
119 pub(crate) name: Option<Cow<'a, str>>,
120
121 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
122 #[builder(default)]
123 pub(crate) _type: Option<Type>,
124}
125
126#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
127#[builder(setter(strip_option))]
128pub struct Local<'a> {
129 #[serde(skip_serializing_if = "Option::is_none")]
130 #[builder(default, setter(into))]
131 pub(crate) domain: Option<Domain<'a>>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
134 #[builder(default, setter(into))]
135 pub(crate) group: Option<LocalGroup<'a>>,
136
137 #[serde(skip_serializing_if = "Option::is_none")]
138 #[builder(default, setter(into))]
139 pub(crate) group_ids: Option<Cow<'a, str>>,
140
141 #[serde(skip_serializing_if = "Option::is_none")]
142 #[builder(default, setter(into))]
143 pub(crate) groups: Option<Cow<'a, str>>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
146 #[builder(default, setter(into))]
147 pub(crate) projects: Option<Vec<Projects<'a>>>,
148
149 #[serde(skip_serializing_if = "Option::is_none")]
150 #[builder(default, setter(into))]
151 pub(crate) user: Option<User<'a>>,
152}
153
154#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
155#[builder(setter(strip_option))]
156pub struct RemoteAnyOneOfRegexType<'a> {
157 #[serde()]
158 #[builder(setter(into))]
159 pub(crate) any_one_of: Vec<Cow<'a, str>>,
160
161 #[serde(skip_serializing_if = "Option::is_none")]
162 #[builder(default, setter(into))]
163 pub(crate) regex: Option<bool>,
164
165 #[serde(rename = "type")]
166 #[builder(setter(into))]
167 pub(crate) _type: Cow<'a, str>,
168}
169
170#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
171#[builder(setter(strip_option))]
172pub struct RemoteBlacklistRegexType<'a> {
173 #[serde()]
174 #[builder(setter(into))]
175 pub(crate) blacklist: Vec<Cow<'a, str>>,
176
177 #[serde(skip_serializing_if = "Option::is_none")]
178 #[builder(default, setter(into))]
179 pub(crate) regex: Option<bool>,
180
181 #[serde(rename = "type")]
182 #[builder(setter(into))]
183 pub(crate) _type: Cow<'a, str>,
184}
185
186#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
187#[builder(setter(strip_option))]
188pub struct RemoteNotAnyOfRegexType<'a> {
189 #[serde()]
190 #[builder(setter(into))]
191 pub(crate) not_any_of: Vec<Cow<'a, str>>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
194 #[builder(default, setter(into))]
195 pub(crate) regex: Option<bool>,
196
197 #[serde(rename = "type")]
198 #[builder(setter(into))]
199 pub(crate) _type: Cow<'a, str>,
200}
201
202#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
203#[builder(setter(strip_option))]
204pub struct RemoteRegexTypeWhitelist<'a> {
205 #[serde(skip_serializing_if = "Option::is_none")]
206 #[builder(default, setter(into))]
207 pub(crate) regex: Option<bool>,
208
209 #[serde(rename = "type")]
210 #[builder(setter(into))]
211 pub(crate) _type: Cow<'a, str>,
212
213 #[serde()]
214 #[builder(setter(into))]
215 pub(crate) whitelist: Vec<Cow<'a, str>>,
216}
217
218#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
219#[builder(setter(strip_option))]
220pub struct RemoteType<'a> {
221 #[serde(rename = "type")]
222 #[builder(setter(into))]
223 pub(crate) _type: Cow<'a, str>,
224}
225
226#[derive(Debug, Deserialize, Clone, Serialize)]
227#[serde(untagged)]
228pub enum RulesRemote<'a> {
229 F1(RemoteAnyOneOfRegexType<'a>),
230 F2(RemoteBlacklistRegexType<'a>),
231 F3(RemoteNotAnyOfRegexType<'a>),
232 F4(RemoteRegexTypeWhitelist<'a>),
233 F5(RemoteType<'a>),
234}
235
236#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
237#[builder(setter(strip_option))]
238pub struct Rules<'a> {
239 #[serde()]
240 #[builder(setter(into))]
241 pub(crate) local: Vec<Local<'a>>,
242
243 #[serde()]
244 #[builder(setter(into))]
245 pub(crate) remote: Vec<RulesRemote<'a>>,
246}
247
248#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
249#[builder(setter(strip_option))]
250pub struct Mapping<'a> {
251 #[serde()]
252 #[builder(setter(into))]
253 pub(crate) rules: Vec<Rules<'a>>,
254
255 #[serde(skip_serializing_if = "Option::is_none")]
257 #[builder(default, setter(into))]
258 pub(crate) schema_version: Option<Cow<'a, str>>,
259}
260
261#[derive(Builder, Debug, Clone)]
262#[builder(setter(strip_option))]
263pub struct Request<'a> {
264 #[builder(setter(into))]
265 pub(crate) mapping: Mapping<'a>,
266
267 #[builder(default, setter(into))]
269 id: Cow<'a, str>,
270
271 #[builder(setter(name = "_headers"), default, private)]
272 _headers: Option<HeaderMap>,
273}
274impl<'a> Request<'a> {
275 pub fn builder() -> RequestBuilder<'a> {
277 RequestBuilder::default()
278 }
279}
280
281impl<'a> RequestBuilder<'a> {
282 pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
284 where
285 K: Into<HeaderName>,
286 V: Into<HeaderValue>,
287 {
288 self._headers
289 .get_or_insert(None)
290 .get_or_insert_with(HeaderMap::new)
291 .insert(header_name.into(), header_value.into());
292 self
293 }
294
295 pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
297 where
298 I: Iterator<Item = T>,
299 T: Into<(Option<HeaderName>, HeaderValue)>,
300 {
301 self._headers
302 .get_or_insert(None)
303 .get_or_insert_with(HeaderMap::new)
304 .extend(iter.map(Into::into));
305 self
306 }
307}
308
309impl RestEndpoint for Request<'_> {
310 fn method(&self) -> http::Method {
311 http::Method::PATCH
312 }
313
314 fn endpoint(&self) -> Cow<'static, str> {
315 format!("OS-FEDERATION/mappings/{id}", id = self.id.as_ref(),).into()
316 }
317
318 fn parameters(&self) -> QueryParams<'_> {
319 QueryParams::default()
320 }
321
322 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
323 let mut params = JsonBodyParams::default();
324
325 params.push("mapping", serde_json::to_value(&self.mapping)?);
326
327 params.into_body()
328 }
329
330 fn service_type(&self) -> ServiceType {
331 ServiceType::Identity
332 }
333
334 fn response_key(&self) -> Option<Cow<'static, str>> {
335 Some("mapping".into())
336 }
337
338 fn request_headers(&self) -> Option<&HeaderMap> {
340 self._headers.as_ref()
341 }
342
343 fn api_version(&self) -> Option<ApiVersion> {
345 Some(ApiVersion::new(3, 0))
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352 use http::{HeaderName, HeaderValue};
353 use httpmock::MockServer;
354 #[cfg(feature = "sync")]
355 use openstack_sdk_core::api::Query;
356 use openstack_sdk_core::test::client::FakeOpenStackClient;
357 use openstack_sdk_core::types::ServiceType;
358 use serde_json::json;
359
360 #[test]
361 fn test_service_type() {
362 assert_eq!(
363 Request::builder()
364 .mapping(
365 MappingBuilder::default()
366 .rules(Vec::from([RulesBuilder::default()
367 .local(Vec::from([LocalBuilder::default().build().unwrap()]))
368 .remote(Vec::from([RulesRemote::F1(
369 RemoteAnyOneOfRegexTypeBuilder::default()
370 ._type("foo")
371 .any_one_of(Vec::from(["foo".into()]))
372 .build()
373 .unwrap()
374 )]))
375 .build()
376 .unwrap()]))
377 .build()
378 .unwrap()
379 )
380 .build()
381 .unwrap()
382 .service_type(),
383 ServiceType::Identity
384 );
385 }
386
387 #[test]
388 fn test_response_key() {
389 assert_eq!(
390 Request::builder()
391 .mapping(
392 MappingBuilder::default()
393 .rules(Vec::from([RulesBuilder::default()
394 .local(Vec::from([LocalBuilder::default().build().unwrap()]))
395 .remote(Vec::from([RulesRemote::F1(
396 RemoteAnyOneOfRegexTypeBuilder::default()
397 ._type("foo")
398 .any_one_of(Vec::from(["foo".into()]))
399 .build()
400 .unwrap()
401 )]))
402 .build()
403 .unwrap()]))
404 .build()
405 .unwrap()
406 )
407 .build()
408 .unwrap()
409 .response_key()
410 .unwrap(),
411 "mapping"
412 );
413 }
414
415 #[cfg(feature = "sync")]
416 #[test]
417 fn endpoint() {
418 let server = MockServer::start();
419 let client = FakeOpenStackClient::new(server.base_url());
420 let mock = server.mock(|when, then| {
421 when.method(httpmock::Method::PATCH)
422 .path(format!("/OS-FEDERATION/mappings/{id}", id = "id",));
423
424 then.status(200)
425 .header("content-type", "application/json")
426 .json_body(json!({ "mapping": {} }));
427 });
428
429 let endpoint = Request::builder()
430 .id("id")
431 .mapping(
432 MappingBuilder::default()
433 .rules(Vec::from([RulesBuilder::default()
434 .local(Vec::from([LocalBuilder::default().build().unwrap()]))
435 .remote(Vec::from([RulesRemote::F1(
436 RemoteAnyOneOfRegexTypeBuilder::default()
437 ._type("foo")
438 .any_one_of(Vec::from(["foo".into()]))
439 .build()
440 .unwrap(),
441 )]))
442 .build()
443 .unwrap()]))
444 .build()
445 .unwrap(),
446 )
447 .build()
448 .unwrap();
449 let _: serde_json::Value = endpoint.query(&client).unwrap();
450 mock.assert();
451 }
452
453 #[cfg(feature = "sync")]
454 #[test]
455 fn endpoint_headers() {
456 let server = MockServer::start();
457 let client = FakeOpenStackClient::new(server.base_url());
458 let mock = server.mock(|when, then| {
459 when.method(httpmock::Method::PATCH)
460 .path(format!("/OS-FEDERATION/mappings/{id}", id = "id",))
461 .header("foo", "bar")
462 .header("not_foo", "not_bar");
463 then.status(200)
464 .header("content-type", "application/json")
465 .json_body(json!({ "mapping": {} }));
466 });
467
468 let endpoint = Request::builder()
469 .id("id")
470 .mapping(
471 MappingBuilder::default()
472 .rules(Vec::from([RulesBuilder::default()
473 .local(Vec::from([LocalBuilder::default().build().unwrap()]))
474 .remote(Vec::from([RulesRemote::F1(
475 RemoteAnyOneOfRegexTypeBuilder::default()
476 ._type("foo")
477 .any_one_of(Vec::from(["foo".into()]))
478 .build()
479 .unwrap(),
480 )]))
481 .build()
482 .unwrap()]))
483 .build()
484 .unwrap(),
485 )
486 .headers(
487 [(
488 Some(HeaderName::from_static("foo")),
489 HeaderValue::from_static("bar"),
490 )]
491 .into_iter(),
492 )
493 .header(
494 HeaderName::from_static("not_foo"),
495 HeaderValue::from_static("not_bar"),
496 )
497 .build()
498 .unwrap();
499 let _: serde_json::Value = endpoint.query(&client).unwrap();
500 mock.assert();
501 }
502}