Skip to main content

openstack_sdk_load_balancer/v2/l7policy/rule/
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 a L7 rule.
19//!
20//! This operation provisions a new L7 rule by using the configuration that you
21//! define in the request object. After the API validates the request and
22//! starts the provisioning process, the API returns a response object that
23//! contains a unique ID and the status of provisioning the L7 rule.
24//!
25//! In the response, the L7 rule [provisioning status](#prov-status) is
26//! `ACTIVE`, `PENDING_CREATE`, or `ERROR`.
27//!
28//! If the status is `PENDING_CREATE`, issue GET
29//! `/v2/lbaas/l7policies/{l7policy_id}/rules/{l7rule_id}` to view the progress
30//! of the provisioning operation. When the L7 rule status changes to `ACTIVE`,
31//! the L7 rule is successfully provisioned and is ready for further
32//! configuration.
33//!
34//! If the API cannot fulfill the request due to insufficient data or data that
35//! is not valid, the service returns the HTTP `Bad Request (400)` response
36//! code with information about the failure in the response body. Validation
37//! errors require that you correct the error and submit the request again.
38//!
39//! All the rules associated with a given policy are logically ANDead together.
40//! A request must match all the policy’s rules to match the policy.
41//!
42//! If you need to express a logical OR operation between rules, then do this
43//! by creating multiple policies with the same action.
44//!
45use derive_builder::Builder;
46use http::{HeaderMap, HeaderName, HeaderValue};
47
48use openstack_sdk_core::api::rest_endpoint_prelude::*;
49
50use serde::Deserialize;
51use serde::Serialize;
52use std::borrow::Cow;
53
54#[derive(Debug, Deserialize, Clone, Serialize)]
55pub enum CompareType {
56    #[serde(rename = "CONTAINS")]
57    Contains,
58    #[serde(rename = "ENDS_WITH")]
59    EndsWith,
60    #[serde(rename = "EQUAL_TO")]
61    EqualTo,
62    #[serde(rename = "REGEX")]
63    Regex,
64    #[serde(rename = "STARTS_WITH")]
65    StartsWith,
66}
67
68#[derive(Debug, Deserialize, Clone, Serialize)]
69pub enum Type {
70    #[serde(rename = "COOKIE")]
71    Cookie,
72    #[serde(rename = "FILE_TYPE")]
73    FileType,
74    #[serde(rename = "HEADER")]
75    Header,
76    #[serde(rename = "HOST_NAME")]
77    HostName,
78    #[serde(rename = "PATH")]
79    Path,
80    #[serde(rename = "SSL_CONN_HAS_CERT")]
81    SslConnHasCert,
82    #[serde(rename = "SSL_DN_FIELD")]
83    SslDnField,
84    #[serde(rename = "SSL_VERIFY_RESULT")]
85    SslVerifyResult,
86}
87
88/// Defines mandatory and optional attributes of a POST request.
89#[derive(Builder, Debug, Deserialize, Clone, Serialize)]
90#[builder(setter(strip_option))]
91pub struct Rule<'a> {
92    /// The administrative state of the resource, which is up (`true`) or down
93    /// (`false`). Default is `true`.
94    #[serde(skip_serializing_if = "Option::is_none")]
95    #[builder(default, setter(into))]
96    pub(crate) admin_state_up: Option<bool>,
97
98    /// The comparison type for the L7 rule. One of `CONTAINS`, `ENDS_WITH`,
99    /// `EQUAL_TO`, `REGEX`, or `STARTS_WITH`.
100    #[serde()]
101    #[builder()]
102    pub(crate) compare_type: CompareType,
103
104    /// When `true` the logic of the rule is inverted. For example, with invert
105    /// `true`, equal to would become not equal to. Default is `false`.
106    #[serde(skip_serializing_if = "Option::is_none")]
107    #[builder(default, setter(into))]
108    pub(crate) invert: Option<bool>,
109
110    /// The key to use for the comparison. For example, the name of the cookie
111    /// to evaluate.
112    #[serde(skip_serializing_if = "Option::is_none")]
113    #[builder(default, setter(into))]
114    pub(crate) key: Option<Cow<'a, str>>,
115
116    /// The ID of the project owning this resource.
117    #[serde(skip_serializing_if = "Option::is_none")]
118    #[builder(default, setter(into))]
119    pub(crate) project_id: Option<Cow<'a, str>>,
120
121    /// A list of simple strings assigned to the resource.
122    ///
123    /// **New in version 2.5**
124    #[serde(skip_serializing_if = "Option::is_none")]
125    #[builder(default, setter(into))]
126    pub(crate) tags: Option<Vec<Cow<'a, str>>>,
127
128    #[serde(skip_serializing_if = "Option::is_none")]
129    #[builder(default, setter(into))]
130    pub(crate) tenant_id: Option<Cow<'a, str>>,
131
132    /// The L7 rule type. One of `COOKIE`, `FILE_TYPE`, `HEADER`, `HOST_NAME`,
133    /// `PATH`, `SSL_CONN_HAS_CERT`, `SSL_VERIFY_RESULT`, or `SSL_DN_FIELD`.
134    #[serde(rename = "type")]
135    #[builder()]
136    pub(crate) _type: Type,
137
138    /// The value to use for the comparison. For example, the file type to
139    /// compare.
140    #[serde()]
141    #[builder(setter(into))]
142    pub(crate) value: Cow<'a, str>,
143}
144
145#[derive(Builder, Debug, Clone)]
146#[builder(setter(strip_option))]
147pub struct Request<'a> {
148    /// Defines mandatory and optional attributes of a POST request.
149    #[builder(setter(into))]
150    pub(crate) rule: Rule<'a>,
151
152    /// l7policy_id parameter for
153    /// /v2/lbaas/l7policies/{l7policy_id}/rules/{rule_id} API
154    #[builder(default, setter(into))]
155    l7policy_id: Cow<'a, str>,
156
157    #[builder(setter(name = "_headers"), default, private)]
158    _headers: Option<HeaderMap>,
159}
160impl<'a> Request<'a> {
161    /// Create a builder for the endpoint.
162    pub fn builder() -> RequestBuilder<'a> {
163        RequestBuilder::default()
164    }
165}
166
167impl<'a> RequestBuilder<'a> {
168    /// Add a single header to the Rule.
169    pub fn header<K, V>(&mut self, header_name: K, header_value: V) -> &mut Self
170    where
171        K: Into<HeaderName>,
172        V: Into<HeaderValue>,
173    {
174        self._headers
175            .get_or_insert(None)
176            .get_or_insert_with(HeaderMap::new)
177            .insert(header_name.into(), header_value.into());
178        self
179    }
180
181    /// Add multiple headers.
182    pub fn headers<I, T>(&mut self, iter: I) -> &mut Self
183    where
184        I: Iterator<Item = T>,
185        T: Into<(Option<HeaderName>, HeaderValue)>,
186    {
187        self._headers
188            .get_or_insert(None)
189            .get_or_insert_with(HeaderMap::new)
190            .extend(iter.map(Into::into));
191        self
192    }
193}
194
195impl RestEndpoint for Request<'_> {
196    fn method(&self) -> http::Method {
197        http::Method::POST
198    }
199
200    fn endpoint(&self) -> Cow<'static, str> {
201        format!(
202            "lbaas/l7policies/{l7policy_id}/rules",
203            l7policy_id = self.l7policy_id.as_ref(),
204        )
205        .into()
206    }
207
208    fn parameters(&self) -> QueryParams<'_> {
209        QueryParams::default()
210    }
211
212    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
213        let mut params = JsonBodyParams::default();
214
215        params.push("rule", serde_json::to_value(&self.rule)?);
216
217        params.into_body()
218    }
219
220    fn service_type(&self) -> ServiceType {
221        ServiceType::LoadBalancer
222    }
223
224    fn response_key(&self) -> Option<Cow<'static, str>> {
225        Some("rule".into())
226    }
227
228    /// Returns headers to be set into the request
229    fn request_headers(&self) -> Option<&HeaderMap> {
230        self._headers.as_ref()
231    }
232
233    /// Returns required API version
234    fn api_version(&self) -> Option<ApiVersion> {
235        Some(ApiVersion::new(2, 0))
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242    use http::{HeaderName, HeaderValue};
243    use httpmock::MockServer;
244    #[cfg(feature = "sync")]
245    use openstack_sdk_core::api::Query;
246    use openstack_sdk_core::test::client::FakeOpenStackClient;
247    use openstack_sdk_core::types::ServiceType;
248    use serde_json::json;
249
250    #[test]
251    fn test_service_type() {
252        assert_eq!(
253            Request::builder()
254                .rule(
255                    RuleBuilder::default()
256                        ._type(Type::Cookie)
257                        .compare_type(CompareType::Contains)
258                        .value("foo")
259                        .build()
260                        .unwrap()
261                )
262                .build()
263                .unwrap()
264                .service_type(),
265            ServiceType::LoadBalancer
266        );
267    }
268
269    #[test]
270    fn test_response_key() {
271        assert_eq!(
272            Request::builder()
273                .rule(
274                    RuleBuilder::default()
275                        ._type(Type::Cookie)
276                        .compare_type(CompareType::Contains)
277                        .value("foo")
278                        .build()
279                        .unwrap()
280                )
281                .build()
282                .unwrap()
283                .response_key()
284                .unwrap(),
285            "rule"
286        );
287    }
288
289    #[cfg(feature = "sync")]
290    #[test]
291    fn endpoint() {
292        let server = MockServer::start();
293        let client = FakeOpenStackClient::new(server.base_url());
294        let mock = server.mock(|when, then| {
295            when.method(httpmock::Method::POST).path(format!(
296                "/lbaas/l7policies/{l7policy_id}/rules",
297                l7policy_id = "l7policy_id",
298            ));
299
300            then.status(200)
301                .header("content-type", "application/json")
302                .json_body(json!({ "rule": {} }));
303        });
304
305        let endpoint = Request::builder()
306            .l7policy_id("l7policy_id")
307            .rule(
308                RuleBuilder::default()
309                    ._type(Type::Cookie)
310                    .compare_type(CompareType::Contains)
311                    .value("foo")
312                    .build()
313                    .unwrap(),
314            )
315            .build()
316            .unwrap();
317        let _: serde_json::Value = endpoint.query(&client).unwrap();
318        mock.assert();
319    }
320
321    #[cfg(feature = "sync")]
322    #[test]
323    fn endpoint_headers() {
324        let server = MockServer::start();
325        let client = FakeOpenStackClient::new(server.base_url());
326        let mock = server.mock(|when, then| {
327            when.method(httpmock::Method::POST)
328                .path(format!(
329                    "/lbaas/l7policies/{l7policy_id}/rules",
330                    l7policy_id = "l7policy_id",
331                ))
332                .header("foo", "bar")
333                .header("not_foo", "not_bar");
334            then.status(200)
335                .header("content-type", "application/json")
336                .json_body(json!({ "rule": {} }));
337        });
338
339        let endpoint = Request::builder()
340            .l7policy_id("l7policy_id")
341            .rule(
342                RuleBuilder::default()
343                    ._type(Type::Cookie)
344                    .compare_type(CompareType::Contains)
345                    .value("foo")
346                    .build()
347                    .unwrap(),
348            )
349            .headers(
350                [(
351                    Some(HeaderName::from_static("foo")),
352                    HeaderValue::from_static("bar"),
353                )]
354                .into_iter(),
355            )
356            .header(
357                HeaderName::from_static("not_foo"),
358                HeaderValue::from_static("not_bar"),
359            )
360            .build()
361            .unwrap();
362        let _: serde_json::Value = endpoint.query(&client).unwrap();
363        mock.assert();
364    }
365}