awesome_operates/router/
handler.rs

1use axum::body::Body;
2use axum::extract::Request;
3use axum::Json;
4use serde_json::Value;
5
6#[macro_export]
7macro_rules! method_exchange {
8    ($method:expr, $path:expr, $resp:expr) => {
9        match $method.to_lowercase().as_str() {
10            "get" => $crate::build_method_router!(get, $path, $resp),
11            "post" => $crate::build_method_router!(post, $path, $resp),
12            "delete" => $crate::build_method_router!(delete, $path, $resp),
13            "put" => $crate::build_method_router!(put, $path, $resp),
14            "patch" => $crate::build_method_router!(patch, $path, $resp),
15            _ => $crate::build_method_router!(get, $path, $resp),
16        }
17    };
18}
19
20#[macro_export]
21macro_rules! build_method_router {
22    ($method:ident, $path:expr, $resp:expr) => {
23        axum::routing::$method(|req: axum::extract::Request<axum::body::Body>| async move {
24            $crate::router::handler::handle_openapi_request(req, $resp).await
25        })
26    };
27}
28
29pub async fn handle_openapi_request(req: Request<Body>, mut resp: Value) -> Json<Value> {
30    let (parts, body) = req.into_parts();
31    let bytes = http_body_util::BodyExt::collect(body)
32        .await
33        .unwrap()
34        .to_bytes();
35    tracing::debug!("handle openapi request receive body len {}", bytes.len());
36    let json_body = serde_json::from_slice(&bytes).unwrap_or_else(|_| {
37        tracing::info!("body transfer is not json for {bytes:?}");
38        serde_json::json!({})
39    });
40    resp["request"] = serde_json::json!({
41        "method": parts.method.as_str(),
42        "path": parts.uri.to_string(),
43        "body": json_body
44    });
45    resp["url_args"] = match_url_openapi_path(
46        resp["path_with_prefix"].as_str().unwrap_or_default(),
47        &parts.uri.to_string(),
48    );
49    resp["body_match_list"] = match_body_args(&resp["component"], &json_body);
50    Json(resp)
51}
52
53/// need to remove prefix in true path request
54///  match "/device/:id/:id2/" with "/device/aaa/bbb/?sasajk" one by one
55/// into {"id": "aaa", "id2": "bbb"}
56pub fn match_url_openapi_path(openapi: &str, path: &str) -> Value {
57    let mut resp = serde_json::json!({});
58    if let Some(openapi) = openapi.split('?').next() {
59        if let Some(path) = path.split('?').next() {
60            let openapi_splits = openapi.split('/').collect::<Vec<&str>>();
61            let path_splits = path.split('/').collect::<Vec<&str>>();
62            for (i, s) in openapi_splits.iter().enumerate() {
63                if s.starts_with(':') {
64                    resp[s.replace(':', "")] = serde_json::json!(path_splits.get(i));
65                }
66            }
67        }
68    }
69    serde_json::json!(resp)
70}
71
72/// match body properties field by field with component with body
73pub fn match_body_args(component: &Value, body: &Value) -> Value {
74    tracing::debug!("match body with {body}");
75    let mut resp = vec![];
76    if let Some(properties) = component["properties"].as_object() {
77        for (key, value) in properties.iter() {
78            let body_value = &body[key];
79            if !body_value.is_null() {
80                resp.push(serde_json::json!({
81                    "key": key,
82                    "value": body_value,
83                    "description": value["description"],
84                    "type": value["type"]
85                }))
86            }
87        }
88    }
89    serde_json::json!(resp)
90}