ic_http_certification/cel/
cel_builder.rs

1use super::{
2    CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification,
3    DefaultResponseCertification, DefaultResponseOnlyCelExpression,
4};
5use std::borrow::Cow;
6
7/// A CEL expression builder for creating a default certification expression.
8#[derive(Debug, Clone)]
9pub struct DefaultCelBuilder {}
10
11impl DefaultCelBuilder {
12    /// Create a CEL expression that skips certification entirely.
13    pub fn skip_certification<'a>() -> CelExpression<'a> {
14        CelExpression::Default(DefaultCelExpression::Skip)
15    }
16
17    /// Creates a builder for a CEL expression that will only certify a response.
18    /// Request certification will not be included with this builder.
19    /// See [DefaultResponseOnlyCelBuilder] for more details on this builder's interface.
20    /// See [full_certification](DefaultCelBuilder::full_certification()) for a builder that will certify both the request and response.
21    pub fn response_only_certification<'a>() -> DefaultResponseOnlyCelBuilder<'a> {
22        DefaultResponseOnlyCelBuilder::default()
23    }
24
25    /// Creates a builder for a CEL expression that will certify both the request and response.
26    /// See [DefaultFullCelExpressionBuilder] for more details on this builder's interface.
27    /// See [response_only_certification](DefaultCelBuilder::response_only_certification()) for a builder that will only certify the response.
28    pub fn full_certification<'a>() -> DefaultFullCelExpressionBuilder<'a> {
29        DefaultFullCelExpressionBuilder::default()
30    }
31}
32
33/// A CEL expression builder for creating expressions that will only certify a response.
34/// To create an expression that certifies both the request and response, see [DefaultFullCelExpressionBuilder].
35#[derive(Debug, Clone, Default)]
36pub struct DefaultResponseOnlyCelBuilder<'a> {
37    response_certification: DefaultResponseCertification<'a>,
38}
39
40impl<'a> DefaultResponseOnlyCelBuilder<'a> {
41    /// Configure the response headers that will be included in certification.
42    ///
43    /// See [DefaultResponseCertification] for details on how to configure this.
44    /// Not calling this method will result in no response headers being certified.
45    pub fn with_response_certification(
46        mut self,
47        headers_config: DefaultResponseCertification<'a>,
48    ) -> Self {
49        self.response_certification = headers_config;
50
51        self
52    }
53
54    /// Build the CEL expression, consuming the builder.
55    pub fn build(self) -> DefaultResponseOnlyCelExpression<'a> {
56        DefaultResponseOnlyCelExpression {
57            response: self.response_certification,
58        }
59    }
60}
61
62/// A CEL expression builder for creating expressions that will certify both the request and response.
63/// To create an expression that only certifies the response, see [DefaultResponseOnlyCelBuilder].
64#[derive(Debug, Clone, Default)]
65pub struct DefaultFullCelExpressionBuilder<'a> {
66    request_headers: Cow<'a, [&'a str]>,
67    request_query_parameters: Cow<'a, [&'a str]>,
68    response_certification: DefaultResponseCertification<'a>,
69}
70
71impl<'a> DefaultFullCelExpressionBuilder<'a> {
72    /// Configure the request headers that will be included in certification.
73    ///
74    /// As many or as little headers can be provided as desired.
75    /// Providing an empty list, or not calling this method, will result in no request query parameters being certified.
76    pub fn with_request_headers(mut self, headers: impl Into<Cow<'a, [&'a str]>>) -> Self {
77        self.request_headers = headers.into();
78
79        self
80    }
81
82    /// Configure the request query parameters that will be included in certification.
83    ///
84    /// As many or as little query parameters can be provided as desired.
85    /// Providing an empty list, or not calling this method, will result in no request query parameters being certified.
86    pub fn with_request_query_parameters(
87        mut self,
88        query_params: impl Into<Cow<'a, [&'a str]>>,
89    ) -> Self {
90        self.request_query_parameters = query_params.into();
91
92        self
93    }
94
95    /// Configure the response headers that will be included in certification.
96    ///
97    /// See [DefaultResponseCertification] for details on how to configure this.
98    /// Not calling this method will result in no response headers being certified.
99    pub fn with_response_certification(
100        mut self,
101        headers_config: DefaultResponseCertification<'a>,
102    ) -> Self {
103        self.response_certification = headers_config;
104
105        self
106    }
107
108    /// Build the CEL expression, consuming the builder.
109    pub fn build(self) -> DefaultFullCelExpression<'a> {
110        let request_certification =
111            DefaultRequestCertification::new(self.request_headers, self.request_query_parameters);
112
113        DefaultFullCelExpression {
114            request: request_certification,
115            response: self.response_certification,
116        }
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::cel::fixtures::*;
124    use rstest::*;
125
126    #[rstest]
127    fn no_certification(no_certification_cel: String) {
128        let cel_expr = DefaultCelBuilder::skip_certification().to_string();
129
130        assert_eq!(cel_expr, no_certification_cel);
131    }
132
133    #[rstest]
134    fn no_request_response_inclusions(no_request_response_inclusions_cel: String) {
135        let cel_expr = DefaultCelBuilder::response_only_certification()
136            .with_response_certification(DefaultResponseCertification::certified_response_headers(
137                vec![
138                    "Cache-Control",
139                    "ETag",
140                    "Content-Length",
141                    "Content-Type",
142                    "Content-Encoding",
143                ],
144            ))
145            .build()
146            .to_string();
147
148        assert_eq!(cel_expr, no_request_response_inclusions_cel);
149    }
150
151    #[rstest]
152    fn no_request_response_exclusions(no_request_response_exclusions_cel: String) {
153        let cel_expr = DefaultCelBuilder::response_only_certification()
154            .with_response_certification(DefaultResponseCertification::response_header_exclusions(
155                vec!["Date", "Cookie", "Set-Cookie"],
156            ))
157            .build()
158            .to_string();
159
160        assert_eq!(cel_expr, no_request_response_exclusions_cel);
161    }
162
163    #[rstest]
164    fn no_request_empty_response_inclusions(no_request_empty_response_inclusions_cel: String) {
165        let implicit_cel_expr = DefaultCelBuilder::response_only_certification()
166            .build()
167            .to_string();
168        let explicit_cel_expr = DefaultCelBuilder::response_only_certification()
169            .with_response_certification(DefaultResponseCertification::certified_response_headers(
170                vec![],
171            ))
172            .build()
173            .to_string();
174        let default_cel_expr = DefaultCelBuilder::response_only_certification()
175            .with_response_certification(DefaultResponseCertification::default())
176            .build()
177            .to_string();
178
179        assert_eq!(implicit_cel_expr, no_request_empty_response_inclusions_cel);
180        assert_eq!(explicit_cel_expr, no_request_empty_response_inclusions_cel);
181        assert_eq!(default_cel_expr, no_request_empty_response_inclusions_cel);
182    }
183
184    #[rstest]
185    fn no_request_empty_response_exclusions(no_request_empty_response_exclusions_cel: String) {
186        let cel_expr = DefaultCelBuilder::response_only_certification()
187            .with_response_certification(DefaultResponseCertification::response_header_exclusions(
188                vec![],
189            ))
190            .build()
191            .to_string();
192
193        assert_eq!(cel_expr, no_request_empty_response_exclusions_cel);
194    }
195
196    #[rstest]
197    fn include_request_response_header_inclusions(
198        include_request_response_header_inclusions_cel: String,
199    ) {
200        let cel_expr = DefaultCelBuilder::full_certification()
201            .with_request_headers(vec!["Accept", "Accept-Encoding", "If-Match"])
202            .with_request_query_parameters(vec!["foo", "bar", "baz"])
203            .with_response_certification(DefaultResponseCertification::certified_response_headers(
204                vec![
205                    "Cache-Control",
206                    "ETag",
207                    "Content-Length",
208                    "Content-Type",
209                    "Content-Encoding",
210                ],
211            ))
212            .build()
213            .to_string();
214
215        assert_eq!(cel_expr, include_request_response_header_inclusions_cel);
216    }
217
218    #[rstest]
219    fn include_request_response_header_exclusions(
220        include_request_response_header_exclusions_cel: String,
221    ) {
222        let cel_expr = DefaultCelBuilder::full_certification()
223            .with_request_headers(vec!["Accept", "Accept-Encoding", "If-Match"])
224            .with_request_query_parameters(vec!["foo", "bar", "baz"])
225            .with_response_certification(DefaultResponseCertification::response_header_exclusions(
226                vec!["Date", "Cookie", "Set-Cookie"],
227            ))
228            .build()
229            .to_string();
230
231        assert_eq!(cel_expr, include_request_response_header_exclusions_cel);
232    }
233
234    #[rstest]
235    fn include_request_empty_response_inclusions(
236        include_request_empty_response_inclusions_cel: String,
237    ) {
238        let implicit_cel_expr = DefaultCelBuilder::full_certification()
239            .with_request_headers(vec!["Accept", "Accept-Encoding", "If-Match"])
240            .with_request_query_parameters(vec!["foo", "bar", "baz"])
241            .build()
242            .to_string();
243        let explicit_cel_expr = DefaultCelBuilder::full_certification()
244            .with_request_headers(vec!["Accept", "Accept-Encoding", "If-Match"])
245            .with_request_query_parameters(vec!["foo", "bar", "baz"])
246            .with_response_certification(DefaultResponseCertification::certified_response_headers(
247                vec![],
248            ))
249            .build()
250            .to_string();
251        let default_cel_expr = DefaultCelBuilder::full_certification()
252            .with_request_headers(vec!["Accept", "Accept-Encoding", "If-Match"])
253            .with_request_query_parameters(vec!["foo", "bar", "baz"])
254            .with_response_certification(DefaultResponseCertification::default())
255            .build()
256            .to_string();
257
258        assert_eq!(
259            implicit_cel_expr,
260            include_request_empty_response_inclusions_cel
261        );
262        assert_eq!(
263            explicit_cel_expr,
264            include_request_empty_response_inclusions_cel
265        );
266        assert_eq!(
267            default_cel_expr,
268            include_request_empty_response_inclusions_cel
269        );
270    }
271
272    #[rstest]
273    fn include_request_empty_response_exclusions(
274        include_request_empty_response_exclusions_cel: String,
275    ) {
276        let cel_expr = DefaultCelBuilder::full_certification()
277            .with_request_headers(vec!["Accept", "Accept-Encoding", "If-Match"])
278            .with_request_query_parameters(vec!["foo", "bar", "baz"])
279            .with_response_certification(DefaultResponseCertification::response_header_exclusions(
280                vec![],
281            ))
282            .build()
283            .to_string();
284
285        assert_eq!(cel_expr, include_request_empty_response_exclusions_cel);
286    }
287
288    #[rstest]
289    fn empty_request_response_inclusions(empty_request_response_inclusions_cel: String) {
290        let implicit_cel_expr = DefaultCelBuilder::full_certification().build().to_string();
291        let explicit_cel_expr = DefaultCelBuilder::full_certification()
292            .with_request_headers(vec![])
293            .with_request_query_parameters(vec![])
294            .with_response_certification(DefaultResponseCertification::certified_response_headers(
295                vec![],
296            ))
297            .build()
298            .to_string();
299        let default_cel_expr = DefaultCelBuilder::full_certification()
300            .with_request_headers(vec![])
301            .with_request_query_parameters(vec![])
302            .with_response_certification(DefaultResponseCertification::default())
303            .build()
304            .to_string();
305
306        assert_eq!(implicit_cel_expr, empty_request_response_inclusions_cel);
307        assert_eq!(explicit_cel_expr, empty_request_response_inclusions_cel);
308        assert_eq!(default_cel_expr, empty_request_response_inclusions_cel);
309    }
310
311    #[rstest]
312    fn empty_request_response_exclusions(empty_request_response_exclusions_cel: String) {
313        let implicit_cel_expr = DefaultCelBuilder::full_certification()
314            .with_response_certification(DefaultResponseCertification::response_header_exclusions(
315                vec![],
316            ))
317            .build()
318            .to_string();
319        let explicit_cel_expr = DefaultCelBuilder::full_certification()
320            .with_request_headers(vec![])
321            .with_request_query_parameters(vec![])
322            .with_response_certification(DefaultResponseCertification::response_header_exclusions(
323                vec![],
324            ))
325            .build()
326            .to_string();
327
328        assert_eq!(implicit_cel_expr, empty_request_response_exclusions_cel);
329        assert_eq!(explicit_cel_expr, empty_request_response_exclusions_cel);
330    }
331}