openstack_sdk/api/compute/v2/server/metadata/
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//! Create or update one or more metadata items for a server.
19//!
20//! Creates any metadata items that do not already exist in the server,
21//! replaces exists metadata items that match keys. Does not modify items that
22//! are not in the request.
23//!
24//! Policy defaults enable only users with the administrative role or the owner
25//! of the server to perform this operation. Cloud providers can change these
26//! permissions through the `policy.json` file.
27//!
28//! Normal response codes: 200
29//!
30//! Error response codes: badRequest(400), unauthorized(401), forbidden(403),
31//! itemNotFound(404), conflict(409)
32//!
33use derive_builder::Builder;
34use http::{HeaderMap, HeaderName, HeaderValue};
35
36use crate::api::rest_endpoint_prelude::*;
37
38use std::borrow::Cow;
39use std::collections::BTreeMap;
40
41#[derive(Builder, Debug, Clone)]
42#[builder(setter(strip_option))]
43pub struct Request<'a> {
44    /// Metadata key and value pairs. The maximum size for each metadata key
45    /// and value pair is 255 bytes.
46    #[builder(private, setter(into, name = "_metadata"))]
47    pub(crate) metadata: BTreeMap<Cow<'a, str>, Cow<'a, str>>,
48
49    /// server_id parameter for /v2.1/servers/{server_id}/metadata/{id} API
50    #[builder(default, setter(into))]
51    server_id: Cow<'a, str>,
52
53    #[builder(setter(name = "_headers"), default, private)]
54    _headers: Option<HeaderMap>,
55}
56impl<'a> Request<'a> {
57    /// Create a builder for the endpoint.
58    pub fn builder() -> RequestBuilder<'a> {
59        RequestBuilder::default()
60    }
61}
62
63impl<'a> RequestBuilder<'a> {
64    /// Metadata key and value pairs. The maximum size for each metadata key
65    /// and value pair is 255 bytes.
66    pub fn metadata<I, K, V>(&mut self, iter: I) -> &mut Self
67    where
68        I: Iterator<Item = (K, V)>,
69        K: Into<Cow<'a, str>>,
70        V: Into<Cow<'a, str>>,
71    {
72        self.metadata
73            .get_or_insert_with(BTreeMap::new)
74            .extend(iter.map(|(k, v)| (k.into(), v.into())));
75        self
76    }
77
78    /// Add a single header to the Metadata.
79    pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
80    where
81        K: Into<HeaderName>,
82        V: Into<HeaderValue>,
83    {
84        self._headers
85            .get_or_insert(None)
86            .get_or_insert_with(HeaderMap::new)
87            .insert(header_name.into(), header_value.into());
88        self
89    }
90
91    /// Add multiple headers.
92    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
93    where
94        I: Iterator<Item = T>,
95        T: Into<(Option<HeaderName>, HeaderValue)>,
96    {
97        self._headers
98            .get_or_insert(None)
99            .get_or_insert_with(HeaderMap::new)
100            .extend(iter.map(Into::into));
101        self
102    }
103}
104
105impl RestEndpoint for Request<'_> {
106    fn method(&self) -> http::Method {
107        http::Method::POST
108    }
109
110    fn endpoint(&self) -> Cow<'static, str> {
111        format!(
112            "servers/{server_id}/metadata",
113            server_id = self.server_id.as_ref(),
114        )
115        .into()
116    }
117
118    fn parameters(&self) -> QueryParams<'_> {
119        QueryParams::default()
120    }
121
122    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
123        let mut params = JsonBodyParams::default();
124
125        params.push("metadata", serde_json::to_value(&self.metadata)?);
126
127        params.into_body()
128    }
129
130    fn service_type(&self) -> ServiceType {
131        ServiceType::Compute
132    }
133
134    fn response_key(&self) -> Option<Cow<'static, str>> {
135        Some("metadata".into())
136    }
137
138    /// Returns headers to be set into the request
139    fn request_headers(&self) -> Option<&HeaderMap> {
140        self._headers.as_ref()
141    }
142
143    /// Returns required API version
144    fn api_version(&self) -> Option<ApiVersion> {
145        Some(ApiVersion::new(2, 1))
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    #[cfg(feature = "sync")]
153    use crate::api::Query;
154    use crate::test::client::FakeOpenStackClient;
155    use crate::types::ServiceType;
156    use http::{HeaderName, HeaderValue};
157    use httpmock::MockServer;
158    use serde_json::json;
159
160    #[test]
161    fn test_service_type() {
162        assert_eq!(
163            Request::builder()
164                .metadata(BTreeMap::<String, String>::new().into_iter())
165                .build()
166                .unwrap()
167                .service_type(),
168            ServiceType::Compute
169        );
170    }
171
172    #[test]
173    fn test_response_key() {
174        assert_eq!(
175            Request::builder()
176                .metadata(BTreeMap::<String, String>::new().into_iter())
177                .build()
178                .unwrap()
179                .response_key()
180                .unwrap(),
181            "metadata"
182        );
183    }
184
185    #[cfg(feature = "sync")]
186    #[test]
187    fn endpoint() {
188        let server = MockServer::start();
189        let client = FakeOpenStackClient::new(server.base_url());
190        let mock = server.mock(|when, then| {
191            when.method(httpmock::Method::POST).path(format!(
192                "/servers/{server_id}/metadata",
193                server_id = "server_id",
194            ));
195
196            then.status(200)
197                .header("content-type", "application/json")
198                .json_body(json!({ "metadata": {} }));
199        });
200
201        let endpoint = Request::builder()
202            .server_id("server_id")
203            .metadata(BTreeMap::<String, String>::new().into_iter())
204            .build()
205            .unwrap();
206        let _: serde_json::Value = endpoint.query(&client).unwrap();
207        mock.assert();
208    }
209
210    #[cfg(feature = "sync")]
211    #[test]
212    fn endpoint_headers() {
213        let server = MockServer::start();
214        let client = FakeOpenStackClient::new(server.base_url());
215        let mock = server.mock(|when, then| {
216            when.method(httpmock::Method::POST)
217                .path(format!(
218                    "/servers/{server_id}/metadata",
219                    server_id = "server_id",
220                ))
221                .header("foo", "bar")
222                .header("not_foo", "not_bar");
223            then.status(200)
224                .header("content-type", "application/json")
225                .json_body(json!({ "metadata": {} }));
226        });
227
228        let endpoint = Request::builder()
229            .server_id("server_id")
230            .metadata(BTreeMap::<String, String>::new().into_iter())
231            .headers(
232                [(
233                    Some(HeaderName::from_static("foo")),
234                    HeaderValue::from_static("bar"),
235                )]
236                .into_iter(),
237            )
238            .header(
239                HeaderName::from_static("not_foo"),
240                HeaderValue::from_static("not_bar"),
241            )
242            .build()
243            .unwrap();
244        let _: serde_json::Value = endpoint.query(&client).unwrap();
245        mock.assert();
246    }
247}