Skip to main content

dingtalk_stream/messages/
graph.rs

1//! Graph API 消息类型,对齐 Python graph.py
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Graph 消息常量
7pub struct GraphMessage;
8
9impl GraphMessage {
10    /// Graph API 调用主题
11    pub const TOPIC: &'static str = "/v1.0/graph/api/invoke";
12}
13
14/// 请求行
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct RequestLine {
17    /// HTTP 方法
18    #[serde(default = "default_method")]
19    pub method: String,
20    /// 请求 URI
21    #[serde(default = "default_uri")]
22    pub uri: String,
23    /// 扩展字段
24    #[serde(flatten)]
25    pub extensions: HashMap<String, serde_json::Value>,
26}
27
28impl Default for RequestLine {
29    fn default() -> Self {
30        Self {
31            method: "GET".to_owned(),
32            uri: "/".to_owned(),
33            extensions: HashMap::new(),
34        }
35    }
36}
37
38fn default_method() -> String {
39    "GET".to_owned()
40}
41
42fn default_uri() -> String {
43    "/".to_owned()
44}
45
46/// 状态行
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct StatusLine {
49    /// 状态码
50    #[serde(default = "default_status_code")]
51    pub code: u16,
52    /// 原因短语
53    #[serde(rename = "reasonPhrase", default = "default_reason")]
54    pub reason_phrase: String,
55    /// 扩展字段
56    #[serde(flatten)]
57    pub extensions: HashMap<String, serde_json::Value>,
58}
59
60impl Default for StatusLine {
61    fn default() -> Self {
62        Self {
63            code: 200,
64            reason_phrase: "OK".to_owned(),
65            extensions: HashMap::new(),
66        }
67    }
68}
69
70fn default_status_code() -> u16 {
71    200
72}
73
74fn default_reason() -> String {
75    "OK".to_owned()
76}
77
78/// Graph 请求
79#[derive(Debug, Clone, Default, Serialize, Deserialize)]
80pub struct GraphRequest {
81    /// 请求体
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub body: Option<String>,
84    /// 请求行
85    #[serde(rename = "requestLine", default)]
86    pub request_line: RequestLine,
87    /// 请求头
88    #[serde(default)]
89    pub headers: HashMap<String, String>,
90    /// 扩展字段
91    #[serde(flatten)]
92    pub extensions: HashMap<String, serde_json::Value>,
93}
94
95/// Graph 响应
96#[derive(Debug, Clone, Default, Serialize, Deserialize)]
97pub struct GraphResponse {
98    /// 响应体
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub body: Option<String>,
101    /// 响应头
102    #[serde(default)]
103    pub headers: HashMap<String, String>,
104    /// 状态行
105    #[serde(rename = "statusLine", default)]
106    pub status_line: StatusLine,
107    /// 扩展字段
108    #[serde(flatten)]
109    pub extensions: HashMap<String, serde_json::Value>,
110}
111
112#[cfg(test)]
113mod tests {
114    use super::*;
115
116    #[test]
117    fn test_graph_request_deserialize() {
118        let json = r#"{
119            "body": "{\"key\":\"value\"}",
120            "requestLine": {"method": "POST", "uri": "/api/test"},
121            "headers": {"Content-Type": "application/json"}
122        }"#;
123        let req: GraphRequest = serde_json::from_str(json).unwrap();
124        assert_eq!(req.request_line.method, "POST");
125        assert_eq!(req.request_line.uri, "/api/test");
126        assert_eq!(req.headers.get("Content-Type").unwrap(), "application/json");
127    }
128
129    #[test]
130    fn test_graph_response_serialize() {
131        let resp = GraphResponse {
132            body: Some(r#"{"result":"ok"}"#.to_owned()),
133            headers: {
134                let mut h = HashMap::new();
135                h.insert("Content-Type".to_owned(), "application/json".to_owned());
136                h
137            },
138            status_line: StatusLine::default(),
139            extensions: HashMap::new(),
140        };
141        let json = serde_json::to_value(&resp).unwrap();
142        assert_eq!(json["statusLine"]["code"], 200);
143        assert_eq!(json["statusLine"]["reasonPhrase"], "OK");
144    }
145
146    #[test]
147    fn test_graph_message_topic() {
148        assert_eq!(GraphMessage::TOPIC, "/v1.0/graph/api/invoke");
149    }
150
151    #[test]
152    fn test_request_line_defaults() {
153        let rl = RequestLine::default();
154        assert_eq!(rl.method, "GET");
155        assert_eq!(rl.uri, "/");
156    }
157}