hive_router_plan_executor/headers/
expression.rs1use std::collections::BTreeMap;
2
3use bytes::Bytes;
4use http::{HeaderMap, HeaderValue};
5use tracing::warn;
6use vrl::core::Value;
7
8use crate::{
9 execution::plan::ClientRequestDetails,
10 headers::{request::RequestExpressionContext, response::ResponseExpressionContext},
11};
12
13fn warn_unsupported_conversion_option<T>(type_name: &str) -> Option<T> {
14 warn!(
15 "Cannot convert VRL {} value to a header value. Please convert it to a string first.",
16 type_name
17 );
18
19 None
20}
21
22pub fn vrl_value_to_header_value(value: Value) -> Option<HeaderValue> {
23 match value {
24 Value::Bytes(bytes) => HeaderValue::from_bytes(&bytes).ok(),
25 Value::Float(f) => HeaderValue::from_str(&f.to_string()).ok(),
26 Value::Boolean(b) => HeaderValue::from_str(if b { "true" } else { "false" }).ok(),
27 Value::Integer(i) => HeaderValue::from_str(&i.to_string()).ok(),
28 Value::Array(_) => warn_unsupported_conversion_option("Array"),
29 Value::Regex(_) => warn_unsupported_conversion_option("Regex"),
30 Value::Timestamp(_) => warn_unsupported_conversion_option("Timestamp"),
31 Value::Object(_) => warn_unsupported_conversion_option("Object"),
32 Value::Null => {
33 warn!("Cannot convert VRL Null value to a header value.");
34 None
35 }
36 }
37}
38
39fn header_map_to_vrl_value(headers: &HeaderMap) -> Value {
40 let mut obj = BTreeMap::new();
41 for (header_name, header_value) in headers.iter() {
42 if let Ok(value) = header_value.to_str() {
43 obj.insert(
44 header_name.as_str().into(),
45 Value::Bytes(Bytes::from(value.to_owned())),
46 );
47 }
48 }
49 Value::Object(obj)
50}
51
52fn client_header_map_to_vrl_value(headers: &ntex_http::HeaderMap) -> Value {
53 let mut obj = BTreeMap::new();
54 for (header_name, header_value) in headers.iter() {
55 if let Ok(value) = header_value.to_str() {
56 obj.insert(
57 header_name.as_str().into(),
58 Value::Bytes(Bytes::from(value.to_owned())),
59 );
60 }
61 }
62 Value::Object(obj)
63}
64
65impl From<&RequestExpressionContext<'_>> for Value {
66 fn from(ctx: &RequestExpressionContext) -> Self {
68 let subgraph_value = {
70 let value = Self::Bytes(Bytes::from(ctx.subgraph_name.to_owned()));
71 Self::Object(BTreeMap::from([("name".into(), value)]))
72 };
73
74 let request_value: Self = ctx.client_request.into();
76
77 Self::Object(BTreeMap::from([
78 ("subgraph".into(), subgraph_value),
79 ("request".into(), request_value),
80 ]))
81 }
82}
83
84impl From<&ResponseExpressionContext<'_>> for Value {
85 fn from(ctx: &ResponseExpressionContext) -> Self {
87 let subgraph_value = Self::Object(BTreeMap::from([(
89 "name".into(),
90 Self::Bytes(Bytes::from(ctx.subgraph_name.to_owned())),
91 )]));
92 let response_value = header_map_to_vrl_value(ctx.subgraph_headers);
94 let request_value: Self = ctx.client_request.into();
96
97 Self::Object(BTreeMap::from([
98 ("subgraph".into(), subgraph_value),
99 ("response".into(), response_value),
100 ("request".into(), request_value),
101 ]))
102 }
103}
104
105impl From<&ClientRequestDetails<'_>> for Value {
106 fn from(details: &ClientRequestDetails) -> Self {
107 let headers_value = client_header_map_to_vrl_value(details.headers);
109
110 let url_value = Self::Object(BTreeMap::from([
112 (
113 "host".into(),
114 details.url.host().unwrap_or("unknown").into(),
115 ),
116 ("path".into(), details.url.path().into()),
117 (
118 "port".into(),
119 details
120 .url
121 .port_u16()
122 .unwrap_or_else(|| {
123 if details.url.scheme() == Some(&http::uri::Scheme::HTTPS) {
124 443
125 } else {
126 80
127 }
128 })
129 .into(),
130 ),
131 ]));
132
133 let operation_value = Self::Object(BTreeMap::from([
135 ("name".into(), details.operation.name.clone().into()),
136 ("type".into(), details.operation.kind.into()),
137 ("query".into(), details.operation.query.clone().into()),
138 ]));
139
140 Self::Object(BTreeMap::from([
141 ("method".into(), details.method.as_str().into()),
142 ("headers".into(), headers_value),
143 ("url".into(), url_value),
144 ("operation".into(), operation_value),
145 ]))
146 }
147}