openlark_docs/common/
api_utils.rs1use openlark_core::{SDKResult, error};
5
6const ERROR_COMPONENT: &str = "openlark-docs";
7
8fn attach_standard_error_context(
9 err: openlark_core::error::CoreError,
10 operation: &str,
11 resource: &str,
12 request_id: Option<String>,
13) -> openlark_core::error::CoreError {
14 err.with_operation(operation, ERROR_COMPONENT)
15 .map_context(|ctx| {
16 ctx.add_context("resource", resource);
17 if let Some(request_id) = request_id.filter(|value| !value.trim().is_empty()) {
18 ctx.set_request_id(request_id);
19 }
20 })
21}
22
23pub fn missing_response_data_error(
25 resource: &str,
26 request_id: Option<String>,
27) -> openlark_core::error::CoreError {
28 attach_standard_error_context(
29 error::validation_error("response.data", "服务器没有返回有效的数据"),
30 "extract_response_data",
31 resource,
32 request_id,
33 )
34}
35
36pub fn request_serialization_error(
38 resource: &str,
39 source: impl std::fmt::Display,
40) -> openlark_core::error::CoreError {
41 attach_standard_error_context(
42 error::validation_error("request.params", format!("无法序列化请求参数: {source}")),
43 "serialize_params",
44 resource,
45 None,
46 )
47}
48pub fn extract_response_data<T>(
58 response: openlark_core::api::ApiResponse<T>,
59 context: &str,
60) -> SDKResult<T> {
61 response.data.ok_or_else(|| {
62 missing_response_data_error(context, response.raw_response.request_id.clone())
63 })
64}
65
66pub fn validate_required_field<T: AsRef<str>>(
77 field_name: &str,
78 field_value: Option<T>,
79 error_message: &str,
80) -> SDKResult<()> {
81 match field_value {
82 Some(value) if !value.as_ref().trim().is_empty() => Ok(()),
83 _ => Err(attach_standard_error_context(
84 error::validation_error(field_name, error_message),
85 "validate_required_field",
86 field_name,
87 None,
88 )),
89 }
90}
91
92#[macro_export]
108macro_rules! api_url {
109 ($base_url:expr) => {
110 $base_url.to_string()
111 };
112 ($base_url:expr, $($arg:expr),+) => {
113 format!($base_url, $($arg),+)
114 };
115}
116
117pub fn serialize_params<T: serde::Serialize>(
127 params: &T,
128 context: &str,
129) -> SDKResult<serde_json::Value> {
130 serde_json::to_value(params).map_err(|e| request_serialization_error(context, e))
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136 use openlark_core::error::ErrorTrait;
137 use serde::Serialize;
138
139 #[derive(Serialize)]
140 struct TestParams {
141 name: String,
142 value: i32,
143 }
144
145 struct FailingParams;
146
147 impl Serialize for FailingParams {
148 fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: serde::Serializer,
151 {
152 Err(serde::ser::Error::custom("boom"))
153 }
154 }
155
156 #[test]
157 fn test_serialize_params_success() {
158 let params = TestParams {
159 name: "test".to_string(),
160 value: 42,
161 };
162 let result = serialize_params(¶ms, "test");
163 assert!(result.is_ok());
164 let json = result.unwrap();
165 assert_eq!(json["name"], "test");
166 assert_eq!(json["value"], 42);
167 }
168
169 #[test]
170 fn test_serialize_params_adds_standard_error_context() {
171 let err = serialize_params(&FailingParams, "查询记录").unwrap_err();
172 assert_eq!(err.context().operation(), Some("serialize_params"));
173 assert_eq!(err.context().component(), Some(ERROR_COMPONENT));
174 assert_eq!(err.context().get_context("resource"), Some("查询记录"));
175 }
176
177 #[test]
178 fn test_extract_response_data_adds_request_id_and_resource_context() {
179 let response = openlark_core::api::ApiResponse::new(
180 None::<serde_json::Value>,
181 openlark_core::api::RawResponse {
182 request_id: Some("req-docs-123".to_string()),
183 ..Default::default()
184 },
185 );
186
187 let err = extract_response_data(response, "查询记录").unwrap_err();
188 assert_eq!(err.context().operation(), Some("extract_response_data"));
189 assert_eq!(err.context().component(), Some(ERROR_COMPONENT));
190 assert_eq!(err.context().get_context("resource"), Some("查询记录"));
191 assert_eq!(err.context().request_id(), Some("req-docs-123"));
192 }
193
194 #[test]
195 fn test_missing_response_data_error_reuses_standard_shape() {
196 let err = missing_response_data_error("获取多维表格", Some("req-docs-456".to_string()));
197 assert_eq!(err.context().operation(), Some("extract_response_data"));
198 assert_eq!(err.context().component(), Some(ERROR_COMPONENT));
199 assert_eq!(err.context().get_context("resource"), Some("获取多维表格"));
200 assert_eq!(err.context().request_id(), Some("req-docs-456"));
201 }
202
203 #[test]
204 fn test_validate_required_field_adds_standard_context() {
205 let err =
206 validate_required_field("sheet_id", None::<String>, "sheet_id 不能为空").unwrap_err();
207 assert_eq!(err.context().operation(), Some("validate_required_field"));
208 assert_eq!(err.context().component(), Some(ERROR_COMPONENT));
209 assert_eq!(err.context().get_context("resource"), Some("sheet_id"));
210 }
211}