-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generic Response mapping using Expr (#349)
* Update examples * Update more info * Update examples * Update wasm-rpc * Initial version of expr transformation * Remove resolved variables * Constructor * Remove ResolvedVariables * Rename resolved variables from path * Fix spec path variables * Form type annotated value from request * Fix test compilation errors * Cache template met * Add typed value function in worker service * Remove compile time error * Start fixing errors * Fix more errors * Fix all main errors * Fix more errors * Temporarily depend on wasm-rpc * Add tests for type infernce * Add comments * Upgrade to latest wasm rpc * Remove json dependency from type annotated value module * Fix more errors * Fix more test cases with 30 remaining * Handle optional field * Fix more errors * Fix evaluator tests with three remaining errors * Fix all tests in evaluator * Fix type inference and add more tests * Fix all tests in http_request * Fix all parser tests * Fix all tests * Fix clippy * Fix clippy * Remove comments * Add more test for type inference * Make the concept of ApiDefinition generic * Move validator to generic * make golem worker binding a generic idea * Start moving everything to an expression module * Start introducing more modules * Start compiling * Initial draft of code organisation * Fix errors * Fix errors * Fix errors * Add constraints * Use Has patterns to support generic * Response binding * First compiled version of draft * Optimise imports * Make modules private and use pub for crate * Tighten privacy in every module * Avoid leaking Response mapping outside base module * Move internal logic to internal module * make sure to use mod internal pattern * Refactor oas_worker_bridge * Further maximise private modules * Try to compile golem worker service * Fix errors * Fix all compile time errors * Compile everything * Make sure everything compile * Fix clippy * Remove unused trait * Fix more errors * Fix more errors * Rename * Rearrange further * Reorganise code * Reformat and rename service * Fix clippy * Reformat and clippy for worker-service * Fix format * Fix formatting * Make ApiDefinition concept generic (#342) * Make the concept of ApiDefinition generic * Move validator to generic * make golem worker binding a generic idea * Start moving everything to an expression module * Start introducing more modules * Start compiling * Initial draft of code organisation * Fix errors * Fix errors * Fix errors * Add constraints * Use Has patterns to support generic * Response binding * First compiled version of draft * Optimise imports * Make modules private and use pub for crate * Tighten privacy in every module * Avoid leaking Response mapping outside base module * Move internal logic to internal module * make sure to use mod internal pattern * Refactor oas_worker_bridge * Further maximise private modules * Try to compile golem worker service * Fix errors * Fix all compile time errors * Compile everything * Make sure everything compile * Fix clippy * Remove unused trait * Fix more errors * Fix more errors * Rename * Rearrange further * Reorganise code * Reformat and rename service * Fix clippy * Reformat and clippy for worker-service * Fix formatting * Fix worker service test * Delete commented code * Update yaml file * Fix construction * Fix construction * Fix none * Add test for nest construction * Add test for Result * Add test * Reformat code * Use proper pattern match * Use err * Generic response mapping * Fix warnings * Remove todo comment * Fix tests * Fix tests * Reformat code * Upgrade golem-examples * Update open api yaml
- Loading branch information
Showing
8 changed files
with
266 additions
and
109 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
228 changes: 228 additions & 0 deletions
228
golem-worker-service-base/src/api_definition/http/http_response_mapping.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,228 @@ | ||
use std::collections::HashMap; | ||
use std::fmt::Display; | ||
|
||
use crate::expression::Expr; | ||
use crate::worker_binding::ResponseMapping; | ||
|
||
#[derive(PartialEq, Debug, Clone)] | ||
pub struct HttpResponseMapping { | ||
pub body: Expr, // ${function.return} | ||
pub status: Expr, // "200" or if ${response.body.id == 1} "200" else "400" | ||
pub headers: HashMap<String, Expr>, | ||
} | ||
|
||
impl Display for HttpResponseMapping { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let response_mapping: ResponseMapping = self.clone().into(); | ||
let expr_json = Expr::to_json_value(&response_mapping.0).unwrap(); | ||
|
||
write!(f, "{}", expr_json) | ||
} | ||
} | ||
|
||
impl From<HttpResponseMapping> for ResponseMapping { | ||
fn from(http_response_mapping: HttpResponseMapping) -> Self { | ||
ResponseMapping(Expr::Record( | ||
vec![ | ||
("body".to_string(), Box::new(http_response_mapping.body)), | ||
("status".to_string(), Box::new(http_response_mapping.status)), | ||
( | ||
"headers".to_string(), | ||
Box::new(Expr::Record( | ||
http_response_mapping | ||
.headers | ||
.into_iter() | ||
.map(|(key, value)| (key, Box::new(value))) | ||
.collect::<Vec<(String, Box<Expr>)>>(), | ||
)), | ||
), | ||
] | ||
.into_iter() | ||
.collect(), | ||
)) | ||
} | ||
} | ||
|
||
impl TryFrom<&ResponseMapping> for HttpResponseMapping { | ||
type Error = String; | ||
|
||
fn try_from(response_mapping: &ResponseMapping) -> Result<Self, Self::Error> { | ||
let mut headers = HashMap::new(); | ||
let generic_expr = &response_mapping.0; | ||
|
||
match generic_expr { | ||
Expr::Record(obj) => { | ||
let mut body = None; | ||
let mut status = None; | ||
|
||
for (key, value) in obj { | ||
match key.as_str() { | ||
"body" => body = Some(value), | ||
"status" => status = Some(value), | ||
"headers" => { | ||
if let Expr::Record(headers_obj) = value.as_ref().clone() { | ||
for (header_key, header_value) in headers_obj { | ||
headers | ||
.insert(header_key.clone(), header_value.as_ref().clone()); | ||
} | ||
} else { | ||
return Err("headers must be an object".to_string()); | ||
} | ||
} | ||
_ => return Err(format!("Unknown key: {}", key)), | ||
} | ||
} | ||
|
||
match (body, status) { | ||
(Some(body), Some(status)) => Ok(HttpResponseMapping { | ||
body: body.as_ref().clone(), | ||
status: status.as_ref().clone(), | ||
headers, | ||
}), | ||
(None, _) => Err("body is required in http response mapping".to_string()), | ||
(_, None) => Err("status is required in http response mapping".to_string()), | ||
} | ||
} | ||
_ => Err("response mapping must be a record".to_string()), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::expression::Expr; | ||
use crate::worker_binding::ResponseMapping; | ||
|
||
#[test] | ||
fn test_try_from_response_mapping() { | ||
let response_mapping = ResponseMapping(Expr::Record( | ||
vec![ | ||
( | ||
"body".to_string(), | ||
Box::new(Expr::Variable("function.return".to_string())), | ||
), | ||
( | ||
"status".to_string(), | ||
Box::new(Expr::Literal("200".to_string())), | ||
), | ||
( | ||
"headers".to_string(), | ||
Box::new(Expr::Record(vec![( | ||
"Content-Type".to_string(), | ||
Box::new(Expr::Literal("application/json".to_string())), | ||
)])), | ||
), | ||
] | ||
.into_iter() | ||
.collect(), | ||
)); | ||
|
||
let http_response_mapping = HttpResponseMapping::try_from(&response_mapping).unwrap(); | ||
|
||
assert_eq!( | ||
http_response_mapping.body, | ||
Expr::Variable("function.return".to_string()) | ||
); | ||
assert_eq!( | ||
http_response_mapping.status, | ||
Expr::Literal("200".to_string()) | ||
); | ||
assert_eq!(http_response_mapping.headers.len(), 1); | ||
assert_eq!( | ||
http_response_mapping.headers.get("Content-Type").unwrap(), | ||
&Expr::Literal("application/json".to_string()) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_try_from_response_mapping_missing_body() { | ||
let response_mapping = ResponseMapping(Expr::Record( | ||
vec![ | ||
( | ||
"status".to_string(), | ||
Box::new(Expr::Literal("200".to_string())), | ||
), | ||
( | ||
"headers".to_string(), | ||
Box::new(Expr::Record(vec![( | ||
"Content-Type".to_string(), | ||
Box::new(Expr::Literal("application/json".to_string())), | ||
)])), | ||
), | ||
] | ||
.into_iter() | ||
.collect(), | ||
)); | ||
|
||
let result = HttpResponseMapping::try_from(&response_mapping); | ||
|
||
assert_eq!( | ||
result, | ||
Err("body is required in http response mapping".to_string()) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_try_from_response_mapping_missing_status() { | ||
let response_mapping = ResponseMapping(Expr::Record( | ||
vec![ | ||
( | ||
"body".to_string(), | ||
Box::new(Expr::Variable("function.return".to_string())), | ||
), | ||
( | ||
"headers".to_string(), | ||
Box::new(Expr::Record(vec![( | ||
"Content-Type".to_string(), | ||
Box::new(Expr::Literal("application/json".to_string())), | ||
)])), | ||
), | ||
] | ||
.into_iter() | ||
.collect(), | ||
)); | ||
|
||
let result = HttpResponseMapping::try_from(&response_mapping); | ||
|
||
assert_eq!( | ||
result, | ||
Err("status is required in http response mapping".to_string()) | ||
); | ||
} | ||
|
||
#[test] | ||
fn test_try_from_response_mapping_headers_not_object() { | ||
let response_mapping = ResponseMapping(Expr::Record( | ||
vec![ | ||
( | ||
"body".to_string(), | ||
Box::new(Expr::Variable("worker.response".to_string())), | ||
), | ||
( | ||
"status".to_string(), | ||
Box::new(Expr::Literal("200".to_string())), | ||
), | ||
( | ||
"headers".to_string(), | ||
Box::new(Expr::Literal("application/json".to_string())), | ||
), | ||
] | ||
.into_iter() | ||
.collect(), | ||
)); | ||
|
||
let result = HttpResponseMapping::try_from(&response_mapping); | ||
|
||
assert_eq!(result, Err("headers must be an object".to_string())); | ||
} | ||
|
||
#[test] | ||
fn test_try_from_response_mapping_not_record() { | ||
let response_mapping = ResponseMapping(Expr::Literal("200".to_string())); | ||
|
||
let result = HttpResponseMapping::try_from(&response_mapping); | ||
|
||
assert_eq!(result, Err("response mapping must be a record".to_string())); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
pub use http_api_definition::*; | ||
pub use http_oas_api_definition::get_api_definition_from_oas; | ||
pub(crate) use http_response_mapping::HttpResponseMapping; | ||
|
||
mod http_api_definition; | ||
mod http_oas_api_definition; | ||
|
||
mod http_response_mapping; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.