Skip to content

Commit aa9691b

Browse files
committed
ref: moved response logic into sperate module
1 parent c6a3445 commit aa9691b

2 files changed

Lines changed: 165 additions & 1 deletion

File tree

adapter/rest/src/response.rs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
use http_body_util::Full;
2+
use hyper::{
3+
Response, StatusCode,
4+
body::Bytes,
5+
header::{HeaderName, HeaderValue},
6+
};
7+
use std::collections::HashMap;
8+
use tucana::shared::{
9+
Struct, Value,
10+
value::Kind::{self, StructValue},
11+
};
12+
13+
use crate::content_type;
14+
15+
pub fn error_to_http_response(status: StatusCode, msg: &str) -> Response<Full<Bytes>> {
16+
let body = format!(r#"{{"error": "{}"}}"#, msg);
17+
Response::builder()
18+
.status(status)
19+
.header("content-type", "application/json")
20+
.body(Full::new(Bytes::from(body)))
21+
.unwrap()
22+
}
23+
24+
pub fn value_to_http_response(value: Value) -> Response<Full<Bytes>> {
25+
let Value {
26+
kind: Some(StructValue(Struct { fields })),
27+
} = value
28+
else {
29+
return error_to_http_response(
30+
StatusCode::INTERNAL_SERVER_ERROR,
31+
"Flow result was not a struct",
32+
);
33+
};
34+
35+
let Some(headers_val) = fields.get("headers") else {
36+
return error_to_http_response(
37+
StatusCode::INTERNAL_SERVER_ERROR,
38+
"Flow result missing headers",
39+
);
40+
};
41+
let Some(status_code_val) = fields.get("http_status_code") else {
42+
return error_to_http_response(
43+
StatusCode::INTERNAL_SERVER_ERROR,
44+
"Flow result missing status_code",
45+
);
46+
};
47+
let Some(payload_val) = fields.get("payload") else {
48+
return error_to_http_response(
49+
StatusCode::INTERNAL_SERVER_ERROR,
50+
"Flow result missing payload",
51+
);
52+
};
53+
54+
// headers struct
55+
let Value {
56+
kind: Some(Kind::StructValue(Struct {
57+
fields: header_fields,
58+
})),
59+
} = headers_val
60+
else {
61+
return error_to_http_response(
62+
StatusCode::INTERNAL_SERVER_ERROR,
63+
"headers was not a list of header entries",
64+
);
65+
};
66+
67+
let mut http_headers: HashMap<String, String> = header_fields
68+
.iter()
69+
.filter_map(|(k, v)| {
70+
if let Value {
71+
kind: Some(Kind::StringValue(x)),
72+
} = v
73+
{
74+
Some((k.clone(), x.clone()))
75+
} else {
76+
None
77+
}
78+
})
79+
.collect();
80+
81+
if find_header_value_case_insensitive(&http_headers, "content-type").is_none() {
82+
http_headers.insert("content-type".to_string(), "application/json".to_string());
83+
}
84+
85+
// status_code number
86+
let Some(Kind::NumberValue(code)) = status_code_val.kind else {
87+
return error_to_http_response(
88+
StatusCode::INTERNAL_SERVER_ERROR,
89+
"status_code was not a number",
90+
);
91+
};
92+
93+
let content_type_header = find_header_value_case_insensitive(&http_headers, "content-type");
94+
let encoded_body = match content_type::encode_body(content_type_header, payload_val.clone()) {
95+
Ok(body) => body,
96+
Err(err) => {
97+
log::error!("Failed to encode response payload: {}", err);
98+
return error_to_http_response(
99+
StatusCode::INTERNAL_SERVER_ERROR,
100+
"Failed to encode response payload",
101+
);
102+
}
103+
};
104+
105+
let http_code = match code.number {
106+
Some(num) => match num {
107+
tucana::shared::number_value::Number::Integer(int) => int as u16,
108+
tucana::shared::number_value::Number::Float(float) => float as u16,
109+
},
110+
None => {
111+
return error_to_http_response(
112+
StatusCode::INTERNAL_SERVER_ERROR,
113+
"Flow execution failed",
114+
);
115+
}
116+
};
117+
118+
let status = StatusCode::from_u16(http_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
119+
create_http_response(status, http_headers, encoded_body)
120+
}
121+
122+
fn find_header_value_case_insensitive<'a>(
123+
headers: &'a HashMap<String, String>,
124+
key: &str,
125+
) -> Option<&'a str> {
126+
headers
127+
.iter()
128+
.find(|(k, _)| k.eq_ignore_ascii_case(key))
129+
.map(|(_, v)| v.as_str())
130+
}
131+
132+
fn create_http_response(
133+
status: StatusCode,
134+
headers: HashMap<String, String>,
135+
body: Vec<u8>,
136+
) -> Response<Full<Bytes>> {
137+
let mut builder = Response::builder().status(status);
138+
139+
{
140+
let h = builder.headers_mut().unwrap();
141+
for (k, v) in headers {
142+
let name = match HeaderName::from_bytes(k.as_bytes()) {
143+
Ok(n) => n,
144+
Err(_) => {
145+
log::warn!("Dropping invalid header name: {}", k);
146+
continue;
147+
}
148+
};
149+
150+
let value = match HeaderValue::from_str(&v) {
151+
Ok(v) => v,
152+
Err(_) => {
153+
log::warn!("Dropping invalid header value for {}: {:?}", k, v);
154+
continue;
155+
}
156+
};
157+
158+
h.insert(name, value);
159+
}
160+
}
161+
162+
builder.body(Full::new(Bytes::from(body))).unwrap()
163+
}

crates/base/src/store.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,15 @@ impl AdapterStore {
121121
// TODO: Replace body vaidation with triangulus when its ready
122122
let uuid = uuid::Uuid::new_v4().to_string();
123123
let flow_id = flow.flow_id;
124-
let execution_flow: ExecutionFlow = Self::convert_validation_flow(flow, input_value);
124+
let execution_flow: ExecutionFlow = Self::convert_validation_flow(flow, input_value.clone());
125125
let bytes = execution_flow.encode_to_vec();
126126
let topic = format!("execution.{}", uuid);
127127
log::info!(
128128
"Requesting execution of flow {} with execution id {}",
129129
flow_id,
130130
uuid
131131
);
132+
log::debug!("Flow Input for Execution ({}) is {:?}", uuid, input_value);
132133
let result = self.client.request(topic, bytes.into()).await;
133134

134135
match result {

0 commit comments

Comments
 (0)