ic_http_certification/cel/
create_cel_expr.rs

1use super::{
2    CelExpression, DefaultCelExpression, DefaultFullCelExpression, DefaultRequestCertification,
3    DefaultResponseCertification, DefaultResponseCertificationType,
4    DefaultResponseOnlyCelExpression,
5};
6
7/// Converts a CEL expression from a [CelExpression] struct into it's [String] representation.
8///
9/// [CelExpression::to_string](ToString::to_string()) is an alias of this function and can be used
10/// for ergonomics.
11pub fn create_cel_expr(certification: &CelExpression) -> String {
12    match certification {
13        CelExpression::Default(certification) => create_default_cel_expr(certification),
14    }
15}
16
17/// Converts a CEL expression from a [DefaultCelExpression] struct into it's [String] representation.
18///
19/// [DefaultCelExpression::to_string](ToString::to_string()) is an alias of this function and
20/// can be used for ergonomics.
21pub fn create_default_cel_expr(certification: &DefaultCelExpression) -> String {
22    match certification {
23        DefaultCelExpression::Skip => create_default_skip_cel_expr(),
24        DefaultCelExpression::ResponseOnly(certification) => {
25            create_default_response_only_cel_expr(certification)
26        }
27        DefaultCelExpression::Full(certification) => create_default_full_cel_expr(certification),
28    }
29}
30
31/// Creates the [String] representation of a CEL expression that skips certification entirely.
32pub fn create_default_skip_cel_expr() -> String {
33    let mut cel_expr = String::from("default_certification(ValidationArgs{");
34    cel_expr.push_str("no_certification:Empty{}");
35    cel_expr.push_str("})");
36    cel_expr
37}
38
39/// Converts a CEL expression that only certifies the [HttpResponse](crate::HttpResponse), excluding the
40/// [HttpRequest](crate::HttpRequest) from certification, from a [DefaultResponseOnlyCelExpression] struct into
41/// it's [String] representation.
42///
43/// [DefaultResponseOnlyCelExpression::to_string](ToString::to_string()) is an
44/// alias of this method and can be used for ergonomics.
45pub fn create_default_response_only_cel_expr(
46    certification: &DefaultResponseOnlyCelExpression,
47) -> String {
48    let mut cel_expr = String::from("default_certification(ValidationArgs{");
49
50    cel_expr.push_str("certification:Certification{");
51    cel_expr.push_str("no_request_certification:Empty{},");
52
53    create_response_cel_expr(&mut cel_expr, &certification.response);
54
55    cel_expr.push('}');
56
57    cel_expr.push_str("})");
58    cel_expr
59}
60
61/// Converts a CEL expression that certifies both the [HttpRequest](crate::HttpRequest) and
62/// [HttpResponse](crate::HttpResponse), from a [DefaultFullCelExpression] struct into it's [String] representation.
63/// [DefaultFullCelExpression::to_string](ToString::to_string()) is an alias of this method and can
64/// be used for ergonomics.
65pub fn create_default_full_cel_expr(certification: &DefaultFullCelExpression) -> String {
66    let mut cel_expr = String::from("default_certification(ValidationArgs{");
67
68    cel_expr.push_str("certification:Certification{");
69
70    create_request_cel_expr(&mut cel_expr, &certification.request);
71    create_response_cel_expr(&mut cel_expr, &certification.response);
72
73    cel_expr.push('}');
74
75    cel_expr.push_str("})");
76    cel_expr
77}
78
79fn create_request_cel_expr(
80    cel_expr: &mut String,
81    request_certification: &DefaultRequestCertification,
82) {
83    cel_expr.push_str("request_certification:RequestCertification{certified_request_headers:[");
84
85    if !request_certification.headers.is_empty() {
86        cel_expr.push('"');
87        cel_expr.push_str(&request_certification.headers.join(r#"",""#));
88        cel_expr.push('"');
89    }
90
91    cel_expr.push_str("],certified_query_parameters:[");
92    if !request_certification.query_parameters.is_empty() {
93        cel_expr.push('"');
94        cel_expr.push_str(&request_certification.query_parameters.join(r#"",""#));
95        cel_expr.push('"');
96    }
97
98    cel_expr.push_str("]},");
99}
100
101fn create_response_cel_expr(
102    cel_expr: &mut String,
103    response_certification: &DefaultResponseCertification,
104) {
105    cel_expr.push_str("response_certification:ResponseCertification{");
106
107    let headers = match response_certification.get_type() {
108        DefaultResponseCertificationType::CertifiedResponseHeaders(headers) => {
109            cel_expr.push_str("certified_response_headers");
110            headers
111        }
112        DefaultResponseCertificationType::ResponseHeaderExclusions(headers) => {
113            cel_expr.push_str("response_header_exclusions");
114            headers
115        }
116    };
117
118    cel_expr.push_str(":ResponseHeaderList{headers:[");
119    if !headers.is_empty() {
120        cel_expr.push('"');
121        cel_expr.push_str(&headers.join(r#"",""#));
122        cel_expr.push('"');
123    }
124    cel_expr.push_str("]}}");
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::cel::fixtures::*;
131    use rstest::*;
132
133    #[rstest]
134    #[case::no_certification(no_certification(), no_certification_cel())]
135    #[case::no_request_header_inclusions(
136        no_request_response_inclusions(),
137        no_request_response_inclusions_cel()
138    )]
139    #[case::no_request_response_exclusions(
140        no_request_response_exclusions(),
141        no_request_response_exclusions_cel()
142    )]
143    #[case::no_request_empty_response_inclusions(
144        no_request_empty_response_inclusions(),
145        no_request_empty_response_inclusions_cel()
146    )]
147    #[case::no_request_empty_response_exclusions(
148        no_request_empty_response_exclusions(),
149        no_request_empty_response_exclusions_cel()
150    )]
151    #[case::include_request_response_header_inclusions(
152        include_request_response_header_inclusions(),
153        include_request_response_header_inclusions_cel()
154    )]
155    #[case::include_request_response_header_exclusions(
156        include_request_response_header_exclusions(),
157        include_request_response_header_exclusions_cel()
158    )]
159    #[case::include_request_empty_response_inclusions(
160        include_request_empty_response_inclusions(),
161        include_request_empty_response_inclusions_cel()
162    )]
163    #[case::include_request_empty_response_exclusions(
164        include_request_empty_response_exclusions(),
165        include_request_empty_response_exclusions_cel()
166    )]
167    #[case::empty_request_response_inclusions(
168        empty_request_response_inclusions(),
169        empty_request_response_inclusions_cel()
170    )]
171    #[case::empty_request_response_exclusions(
172        empty_request_response_exclusions(),
173        empty_request_response_exclusions_cel()
174    )]
175    fn create_cel_expr_test(#[case] certification: CelExpression, #[case] expected: String) {
176        let cel_expr = create_cel_expr(&certification);
177
178        assert_eq!(cel_expr, expected);
179    }
180
181    fn no_certification() -> CelExpression<'static> {
182        CelExpression::Default(DefaultCelExpression::Skip)
183    }
184
185    fn no_request_response_inclusions() -> CelExpression<'static> {
186        CelExpression::Default(DefaultCelExpression::ResponseOnly(
187            DefaultResponseOnlyCelExpression {
188                response: DefaultResponseCertification::certified_response_headers(vec![
189                    "Cache-Control",
190                    "ETag",
191                    "Content-Length",
192                    "Content-Type",
193                    "Content-Encoding",
194                ]),
195            },
196        ))
197    }
198
199    fn no_request_response_exclusions() -> CelExpression<'static> {
200        CelExpression::Default(DefaultCelExpression::ResponseOnly(
201            DefaultResponseOnlyCelExpression {
202                response: DefaultResponseCertification::response_header_exclusions(vec![
203                    "Date",
204                    "Cookie",
205                    "Set-Cookie",
206                ]),
207            },
208        ))
209    }
210
211    fn no_request_empty_response_inclusions() -> CelExpression<'static> {
212        CelExpression::Default(DefaultCelExpression::ResponseOnly(
213            DefaultResponseOnlyCelExpression {
214                response: DefaultResponseCertification::certified_response_headers(vec![]),
215            },
216        ))
217    }
218
219    fn no_request_empty_response_exclusions() -> CelExpression<'static> {
220        CelExpression::Default(DefaultCelExpression::ResponseOnly(
221            DefaultResponseOnlyCelExpression {
222                response: DefaultResponseCertification::response_header_exclusions(vec![]),
223            },
224        ))
225    }
226
227    fn include_request_response_header_inclusions() -> CelExpression<'static> {
228        CelExpression::Default(DefaultCelExpression::Full(DefaultFullCelExpression {
229            request: DefaultRequestCertification::new(
230                vec!["Accept", "Accept-Encoding", "If-Match"],
231                vec!["foo", "bar", "baz"],
232            ),
233            response: DefaultResponseCertification::certified_response_headers(vec![
234                "Cache-Control",
235                "ETag",
236                "Content-Length",
237                "Content-Type",
238                "Content-Encoding",
239            ]),
240        }))
241    }
242
243    fn include_request_response_header_exclusions() -> CelExpression<'static> {
244        CelExpression::Default(DefaultCelExpression::Full(DefaultFullCelExpression {
245            request: DefaultRequestCertification::new(
246                vec!["Accept", "Accept-Encoding", "If-Match"],
247                vec!["foo", "bar", "baz"],
248            ),
249            response: DefaultResponseCertification::response_header_exclusions(vec![
250                "Date",
251                "Cookie",
252                "Set-Cookie",
253            ]),
254        }))
255    }
256
257    fn include_request_empty_response_inclusions() -> CelExpression<'static> {
258        CelExpression::Default(DefaultCelExpression::Full(DefaultFullCelExpression {
259            request: DefaultRequestCertification::new(
260                vec!["Accept", "Accept-Encoding", "If-Match"],
261                vec!["foo", "bar", "baz"],
262            ),
263            response: DefaultResponseCertification::certified_response_headers(vec![]),
264        }))
265    }
266
267    fn include_request_empty_response_exclusions() -> CelExpression<'static> {
268        CelExpression::Default(DefaultCelExpression::Full(DefaultFullCelExpression {
269            request: DefaultRequestCertification::new(
270                vec!["Accept", "Accept-Encoding", "If-Match"],
271                vec!["foo", "bar", "baz"],
272            ),
273            response: DefaultResponseCertification::response_header_exclusions(vec![]),
274        }))
275    }
276
277    fn empty_request_response_inclusions() -> CelExpression<'static> {
278        CelExpression::Default(DefaultCelExpression::Full(DefaultFullCelExpression {
279            request: DefaultRequestCertification::new(vec![], vec![]),
280            response: DefaultResponseCertification::certified_response_headers(vec![]),
281        }))
282    }
283
284    fn empty_request_response_exclusions() -> CelExpression<'static> {
285        CelExpression::Default(DefaultCelExpression::Full(DefaultFullCelExpression {
286            request: DefaultRequestCertification::new(vec![], vec![]),
287            response: DefaultResponseCertification::response_header_exclusions(vec![]),
288        }))
289    }
290}