openstack_sdk/api/compute/v2/flavor/extra_spec/
create.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12//
13// SPDX-License-Identifier: Apache-2.0
14//
15// WARNING: This file is automatically generated from OpenAPI schema using
16// `openstack-codegenerator`.
17
18//! Creates extra specs for a flavor, by ID.
19//!
20//! Normal response codes: 200
21//!
22//! Error response codes: unauthorized(401), forbidden(403), itemNotFound(404),
23//! conflict(409)
24//!
25use derive_builder::Builder;
26use http::{HeaderMap, HeaderName, HeaderValue};
27
28use crate::api::rest_endpoint_prelude::*;
29
30use std::borrow::Cow;
31use std::collections::BTreeMap;
32
33#[derive(Builder, Debug, Clone)]
34#[builder(setter(strip_option))]
35pub struct Request<'a> {
36    /// A dictionary of the flavor’s extra-specs key-and-value pairs. It
37    /// appears in the os-extra-specs’ “create” REQUEST body, as well as the
38    /// os-extra-specs’ “create” and “list” RESPONSE body.
39    #[builder(private, setter(into, name = "_extra_specs"))]
40    pub(crate) extra_specs: BTreeMap<Cow<'a, str>, Cow<'a, str>>,
41
42    /// flavor_id parameter for /v2.1/flavors/{flavor_id}/os-extra_specs/{id}
43    /// API
44    #[builder(default, setter(into))]
45    flavor_id: Cow<'a, str>,
46
47    #[builder(setter(name = "_headers"), default, private)]
48    _headers: Option<HeaderMap>,
49}
50impl<'a> Request<'a> {
51    /// Create a builder for the endpoint.
52    pub fn builder() -> RequestBuilder<'a> {
53        RequestBuilder::default()
54    }
55}
56
57impl<'a> RequestBuilder<'a> {
58    /// A dictionary of the flavor’s extra-specs key-and-value pairs. It
59    /// appears in the os-extra-specs’ “create” REQUEST body, as well as the
60    /// os-extra-specs’ “create” and “list” RESPONSE body.
61    pub fn extra_specs<I, K, V>(&mut self, iter: I) -> &mut Self
62    where
63        I: Iterator<Item = (K, V)>,
64        K: Into<Cow<'a, str>>,
65        V: Into<Cow<'a, str>>,
66    {
67        self.extra_specs
68            .get_or_insert_with(BTreeMap::new)
69            .extend(iter.map(|(k, v)| (k.into(), v.into())));
70        self
71    }
72
73    /// Add a single header to the Extra_Spec.
74    pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
75    where
76        K: Into<HeaderName>,
77        V: Into<HeaderValue>,
78    {
79        self._headers
80            .get_or_insert(None)
81            .get_or_insert_with(HeaderMap::new)
82            .insert(header_name.into(), header_value.into());
83        self
84    }
85
86    /// Add multiple headers.
87    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
88    where
89        I: Iterator<Item = T>,
90        T: Into<(Option<HeaderName>, HeaderValue)>,
91    {
92        self._headers
93            .get_or_insert(None)
94            .get_or_insert_with(HeaderMap::new)
95            .extend(iter.map(Into::into));
96        self
97    }
98}
99
100impl RestEndpoint for Request<'_> {
101    fn method(&self) -> http::Method {
102        http::Method::POST
103    }
104
105    fn endpoint(&self) -> Cow<'static, str> {
106        format!(
107            "flavors/{flavor_id}/os-extra_specs",
108            flavor_id = self.flavor_id.as_ref(),
109        )
110        .into()
111    }
112
113    fn parameters(&self) -> QueryParams<'_> {
114        QueryParams::default()
115    }
116
117    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
118        let mut params = JsonBodyParams::default();
119
120        params.push("extra_specs", serde_json::to_value(&self.extra_specs)?);
121
122        params.into_body()
123    }
124
125    fn service_type(&self) -> ServiceType {
126        ServiceType::Compute
127    }
128
129    fn response_key(&self) -> Option<Cow<'static, str>> {
130        Some("extra_specs".into())
131    }
132
133    /// Returns headers to be set into the request
134    fn request_headers(&self) -> Option<&HeaderMap> {
135        self._headers.as_ref()
136    }
137
138    /// Returns required API version
139    fn api_version(&self) -> Option<ApiVersion> {
140        Some(ApiVersion::new(2, 1))
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    #[cfg(feature = "sync")]
148    use crate::api::Query;
149    use crate::test::client::FakeOpenStackClient;
150    use crate::types::ServiceType;
151    use http::{HeaderName, HeaderValue};
152    use httpmock::MockServer;
153    use serde_json::json;
154
155    #[test]
156    fn test_service_type() {
157        assert_eq!(
158            Request::builder()
159                .extra_specs(BTreeMap::<String, String>::new().into_iter())
160                .build()
161                .unwrap()
162                .service_type(),
163            ServiceType::Compute
164        );
165    }
166
167    #[test]
168    fn test_response_key() {
169        assert_eq!(
170            Request::builder()
171                .extra_specs(BTreeMap::<String, String>::new().into_iter())
172                .build()
173                .unwrap()
174                .response_key()
175                .unwrap(),
176            "extra_specs"
177        );
178    }
179
180    #[cfg(feature = "sync")]
181    #[test]
182    fn endpoint() {
183        let server = MockServer::start();
184        let client = FakeOpenStackClient::new(server.base_url());
185        let mock = server.mock(|when, then| {
186            when.method(httpmock::Method::POST).path(format!(
187                "/flavors/{flavor_id}/os-extra_specs",
188                flavor_id = "flavor_id",
189            ));
190
191            then.status(200)
192                .header("content-type", "application/json")
193                .json_body(json!({ "extra_specs": {} }));
194        });
195
196        let endpoint = Request::builder()
197            .flavor_id("flavor_id")
198            .extra_specs(BTreeMap::<String, String>::new().into_iter())
199            .build()
200            .unwrap();
201        let _: serde_json::Value = endpoint.query(&client).unwrap();
202        mock.assert();
203    }
204
205    #[cfg(feature = "sync")]
206    #[test]
207    fn endpoint_headers() {
208        let server = MockServer::start();
209        let client = FakeOpenStackClient::new(server.base_url());
210        let mock = server.mock(|when, then| {
211            when.method(httpmock::Method::POST)
212                .path(format!(
213                    "/flavors/{flavor_id}/os-extra_specs",
214                    flavor_id = "flavor_id",
215                ))
216                .header("foo", "bar")
217                .header("not_foo", "not_bar");
218            then.status(200)
219                .header("content-type", "application/json")
220                .json_body(json!({ "extra_specs": {} }));
221        });
222
223        let endpoint = Request::builder()
224            .flavor_id("flavor_id")
225            .extra_specs(BTreeMap::<String, String>::new().into_iter())
226            .headers(
227                [(
228                    Some(HeaderName::from_static("foo")),
229                    HeaderValue::from_static("bar"),
230                )]
231                .into_iter(),
232            )
233            .header(
234                HeaderName::from_static("not_foo"),
235                HeaderValue::from_static("not_bar"),
236            )
237            .build()
238            .unwrap();
239        let _: serde_json::Value = endpoint.query(&client).unwrap();
240        mock.assert();
241    }
242}