Skip to main content

openlark_core/api/
helpers.rs

1//! 通用 HTTP 管道 helper(canonical copy)。
2//!
3//! 业务 crate 的 `common::api_utils` 应 re-export 此处定义,避免各 crate 各派生一份
4//! 导致 locality 失守(#330)。错误统一经 `map_context` 附加 `operation` / `resource`
5//! (caller 传入的 context)/ `request_id`(响应携带时)结构化诊断上下文。
6
7use crate::SDKResult;
8use crate::api::Response;
9use crate::error::validation_error;
10
11/// 标准化参数序列化。
12///
13/// 序列化失败时返回 `validation_error`,附加 `operation=serialize_params` + `resource=<context>`。
14pub fn serialize_params<T: serde::Serialize>(
15    params: &T,
16    context: &str,
17) -> SDKResult<serde_json::Value> {
18    serde_json::to_value(params).map_err(|e| {
19        validation_error("request.params", format!("无法序列化请求参数: {e}")).map_context(|ctx| {
20            ctx.set_operation("serialize_params")
21                .add_context("resource", context);
22        })
23    })
24}
25
26/// 标准化响应数据提取。
27///
28/// 响应 `data` 为空时返回 `validation_error`,附加 `operation=extract_response_data` +
29/// `resource=<context>` + 响应携带的 `request_id`。
30pub fn extract_response_data<T>(response: Response<T>, context: &str) -> SDKResult<T> {
31    let request_id = response.raw_response.request_id.clone();
32    response.data.ok_or_else(|| {
33        validation_error("response.data", "服务器没有返回有效的数据").map_context(|ctx| {
34            ctx.set_operation("extract_response_data")
35                .add_context("resource", context);
36            if let Some(req_id) = request_id.as_ref().filter(|r| !r.trim().is_empty()) {
37                ctx.set_request_id(req_id);
38            }
39        })
40    })
41}
42
43/// 无 data 接口:仅用 `code==0` 判断成功。
44///
45/// 用于响应不含 `data` 字段的 API。失败时附加 `operation=ensure_success` + `resource=<context>`。
46pub fn ensure_success(response: Response<serde_json::Value>, context: &str) -> SDKResult<()> {
47    if response.raw_response.is_success() {
48        Ok(())
49    } else {
50        Err(
51            validation_error("response.code", response.raw_response.msg.to_string()).map_context(
52                |ctx| {
53                    ctx.set_operation("ensure_success")
54                        .add_context("resource", context);
55                },
56            ),
57        )
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use crate::api::RawResponse;
65
66    #[test]
67    fn serialize_params_success() {
68        let params = vec!["a", "b"];
69        let v = serialize_params(&params, "测试").expect("序列化应成功");
70        assert_eq!(v, serde_json::json!(["a", "b"]));
71    }
72
73    #[test]
74    fn extract_response_data_some() {
75        let response: Response<String> = Response {
76            data: Some("x".to_string()),
77            raw_response: RawResponse::success(),
78        };
79        assert_eq!(extract_response_data(response, "测试").unwrap(), "x");
80    }
81
82    #[test]
83    fn extract_response_data_empty_is_error() {
84        let response: Response<String> = Response {
85            data: None,
86            raw_response: RawResponse::success(),
87        };
88        assert!(extract_response_data(response, "测试").is_err());
89    }
90}