open_lark/service/cloud_docs/sheets/v2/data_operation/
append_data.rs

1use serde::Serialize;
2use serde_json::Value;
3
4use crate::{
5    core::{
6        api_req::ApiRequest, api_resp::BaseResponse, constants::AccessTokenType,
7        endpoints::cloud_docs::*, req_option::RequestOption, SDKResult,
8    },
9    impl_executable_builder_owned,
10    service::cloud_docs::sheets::v2::{
11        data_operation::{UpdateSheetDataResponse, ValueRangeRequest},
12        SpreadsheetSheetService,
13    },
14};
15
16/// 追加数据请求
17#[derive(Serialize, Debug, Default)]
18pub struct AppendDataRequest {
19    #[serde(skip)]
20    api_request: ApiRequest,
21    #[serde(skip)]
22    spreadsheet_token: String,
23    /// 遇到空行追加,默认 OVERWRITE,若空行的数量小于追加数据的行数,则会覆盖已有数据;可选
24    /// INSERT_ROWS ,会在插入足够数量的行后再进行数据追加
25    #[serde(rename = "insertDataOption")]
26    insert_data_option: String,
27    /// 值与范围
28    #[serde(rename = "valueRange")]
29    value_range: ValueRangeRequest,
30}
31
32impl AppendDataRequest {
33    pub fn builder() -> AppendDataRequestBuilder {
34        AppendDataRequestBuilder::default()
35    }
36}
37
38#[derive(Default)]
39pub struct AppendDataRequestBuilder {
40    request: AppendDataRequest,
41}
42
43impl AppendDataRequestBuilder {
44    pub fn spreadsheet_token(mut self, spreadsheet_token: impl ToString) -> Self {
45        self.request.spreadsheet_token = spreadsheet_token.to_string();
46        self
47    }
48
49    /// 插入范围,包含 sheetId 与单元格范围两部分,目前支持四种索引方式,详见
50    /// 在线表格开发指南,range所表示的范围需要大于等于values占用的范围。
51    pub fn range(mut self, range: impl ToString) -> Self {
52        self.request.value_range.range = range.to_string();
53        self
54    }
55
56    /// 需要写入的值,如要写入公式、超链接、email、@人等,可详看附录sheet 支持写入数据类型
57    pub fn values(mut self, values: Value) -> Self {
58        self.request.value_range.values = values;
59        self
60    }
61
62    pub fn build(mut self) -> AppendDataRequest {
63        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
64        self.request
65    }
66}
67
68pub type AppendDataResponse = UpdateSheetDataResponse;
69
70// 使用宏实现ExecutableBuilder trait
71impl_executable_builder_owned!(
72    AppendDataRequestBuilder,
73    SpreadsheetSheetService,
74    AppendDataRequest,
75    BaseResponse<AppendDataResponse>,
76    append_data
77);
78
79impl SpreadsheetSheetService {
80    /// 追加数据
81    pub async fn append_data(
82        &self,
83        request: AppendDataRequest,
84        option: Option<RequestOption>,
85    ) -> SDKResult<BaseResponse<AppendDataResponse>> {
86        let mut api_req = request.api_request;
87        api_req.api_path =
88            SHEETS_V2_SPREADSHEET_VALUES_APPEND.replace("{}", &request.spreadsheet_token);
89        api_req.http_method = reqwest::Method::POST;
90        api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::App];
91
92        let api_resp =
93            crate::core::http::Transport::request(api_req, &self.config_arc, option).await?;
94
95        Ok(api_resp)
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::core::config::Config;
103    use serde_json::{json, Value};
104
105    fn create_test_config() -> Config {
106        Config::builder()
107            .app_id("test_app_id")
108            .app_secret("test_app_secret")
109            .build()
110    }
111
112    fn create_test_service() -> SpreadsheetSheetService {
113        SpreadsheetSheetService::new(create_test_config())
114    }
115
116    #[test]
117    fn test_append_data_request_builder_creation() {
118        let builder = AppendDataRequest::builder();
119        let request = builder.build();
120
121        assert_eq!(request.spreadsheet_token, "");
122        assert_eq!(request.value_range.range, "");
123        assert_eq!(request.value_range.values, Value::Null);
124        assert_eq!(request.insert_data_option, "");
125    }
126
127    #[test]
128    fn test_append_data_request_builder_with_spreadsheet_token() {
129        let request = AppendDataRequest::builder()
130            .spreadsheet_token("test_token_123")
131            .build();
132
133        assert_eq!(request.spreadsheet_token, "test_token_123");
134    }
135
136    #[test]
137    fn test_append_data_request_builder_with_range() {
138        let request = AppendDataRequest::builder().range("Sheet1!A1:C10").build();
139
140        assert_eq!(request.value_range.range, "Sheet1!A1:C10");
141    }
142
143    #[test]
144    fn test_append_data_request_builder_with_values() {
145        let test_values = json!([
146            ["Name", "Age", "City"],
147            ["John", 25, "NYC"],
148            ["Jane", 30, "LA"]
149        ]);
150
151        let request = AppendDataRequest::builder()
152            .values(test_values.clone())
153            .build();
154
155        assert_eq!(request.value_range.values, test_values);
156    }
157
158    #[test]
159    fn test_append_data_request_builder_chaining() {
160        let test_values = json!([["A", "B"], ["1", "2"]]);
161
162        let request = AppendDataRequest::builder()
163            .spreadsheet_token("my_sheet_token")
164            .range("Sheet1!A1:B2")
165            .values(test_values.clone())
166            .build();
167
168        assert_eq!(request.spreadsheet_token, "my_sheet_token");
169        assert_eq!(request.value_range.range, "Sheet1!A1:B2");
170        assert_eq!(request.value_range.values, test_values);
171    }
172
173    #[test]
174    fn test_append_data_request_default() {
175        let request = AppendDataRequest::default();
176
177        assert_eq!(request.spreadsheet_token, "");
178        assert_eq!(request.value_range.range, "");
179        assert_eq!(request.value_range.values, Value::Null);
180        assert_eq!(request.insert_data_option, "");
181    }
182
183    #[test]
184    fn test_append_data_request_builder_default() {
185        let builder = AppendDataRequestBuilder::default();
186        let request = builder.build();
187
188        assert_eq!(request.spreadsheet_token, "");
189        assert_eq!(request.value_range.range, "");
190        assert_eq!(request.value_range.values, Value::Null);
191    }
192
193    #[test]
194    fn test_append_data_request_serialization() {
195        let test_values = json!([["Header1", "Header2"], ["Value1", "Value2"]]);
196
197        let request = AppendDataRequest::builder()
198            .spreadsheet_token("token123")
199            .range("Sheet1!A:B")
200            .values(test_values)
201            .build();
202
203        // Test that the request can be serialized (this validates field names)
204        let serialized = serde_json::to_string(&request);
205        assert!(serialized.is_ok());
206
207        let json_str = serialized.unwrap();
208        assert!(json_str.contains("insertDataOption"));
209        assert!(json_str.contains("valueRange"));
210    }
211
212    #[test]
213    fn test_append_data_request_debug() {
214        let request = AppendDataRequest::builder()
215            .spreadsheet_token("debug_token")
216            .build();
217
218        let debug_str = format!("{:?}", request);
219        assert!(debug_str.contains("AppendDataRequest"));
220        assert!(debug_str.contains("debug_token"));
221    }
222
223    #[test]
224    fn test_append_data_request_with_empty_strings() {
225        let request = AppendDataRequest::builder()
226            .spreadsheet_token("")
227            .range("")
228            .values(Value::Null)
229            .build();
230
231        assert_eq!(request.spreadsheet_token, "");
232        assert_eq!(request.value_range.range, "");
233        assert_eq!(request.value_range.values, Value::Null);
234    }
235
236    #[test]
237    fn test_append_data_request_with_special_characters() {
238        let request = AppendDataRequest::builder()
239            .spreadsheet_token("token_with_特殊字符_🎯")
240            .range("Sheet名称!A1:Z100")
241            .build();
242
243        assert_eq!(request.spreadsheet_token, "token_with_特殊字符_🎯");
244        assert_eq!(request.value_range.range, "Sheet名称!A1:Z100");
245    }
246
247    #[test]
248    fn test_append_data_request_with_unicode_values() {
249        let test_values = json!([
250            ["姓名", "年龄", "城市"],
251            ["张三", 25, "北京"],
252            ["李四", 30, "上海"],
253            ["王五", 35, "深圳"]
254        ]);
255
256        let request = AppendDataRequest::builder()
257            .values(test_values.clone())
258            .build();
259
260        assert_eq!(request.value_range.values, test_values);
261    }
262
263    #[test]
264    fn test_append_data_request_with_complex_json() {
265        let complex_values = json!([
266            [{"text": "Complex"}, {"formula": "=A1+B1"}, {"link": "https://example.com"}],
267            [123.45, true, null],
268            [{"nested": {"deep": "value"}}, [1, 2, 3], "simple_string"]
269        ]);
270
271        let request = AppendDataRequest::builder()
272            .values(complex_values.clone())
273            .build();
274
275        assert_eq!(request.value_range.values, complex_values);
276    }
277
278    #[test]
279    fn test_append_data_request_with_large_data() {
280        let large_values = json!((0..1000)
281            .map(|i| vec![
282                format!("Name_{}", i),
283                i.to_string(),
284                format!("Description for item {}", i)
285            ])
286            .collect::<Vec<_>>());
287
288        let request = AppendDataRequest::builder()
289            .spreadsheet_token("large_data_token")
290            .range("Sheet1!A:C")
291            .values(large_values.clone())
292            .build();
293
294        assert_eq!(request.value_range.values, large_values);
295        assert_eq!(request.spreadsheet_token, "large_data_token");
296    }
297
298    #[test]
299    fn test_append_data_request_with_different_range_formats() {
300        let ranges = vec![
301            "Sheet1!A1:B2",
302            "工作表1!C:D",
303            "'My Sheet'!E5:G10",
304            "Sheet2!A:A",
305            "Sheet3!1:3",
306        ];
307
308        for range in ranges {
309            let request = AppendDataRequest::builder().range(range).build();
310
311            assert_eq!(request.value_range.range, range);
312        }
313    }
314
315    #[test]
316    fn test_append_data_request_with_different_value_types() {
317        let different_types = vec![
318            json!("string"),
319            json!(42),
320            json!(3.14286),
321            json!(true),
322            json!(null),
323            json!([1, 2, 3]),
324            json!({"key": "value"}),
325        ];
326
327        for value in different_types {
328            let request = AppendDataRequest::builder().values(value.clone()).build();
329
330            assert_eq!(request.value_range.values, value);
331        }
332    }
333
334    #[test]
335    fn test_append_data_request_api_request_body_serialization() {
336        let test_values = json!([["A", "B"], ["1", "2"]]);
337
338        let request = AppendDataRequest::builder()
339            .spreadsheet_token("body_test_token")
340            .range("Sheet1!A:B")
341            .values(test_values)
342            .build();
343
344        // Verify that api_request.body is set correctly
345        assert!(!request.api_request.body.is_empty());
346
347        // Verify that the body contains valid JSON
348        let body_str = String::from_utf8(request.api_request.body).unwrap();
349        let parsed: Value = serde_json::from_str(&body_str).unwrap();
350
351        assert!(parsed.get("valueRange").is_some());
352        assert!(parsed.get("insertDataOption").is_some());
353    }
354
355    #[test]
356    fn test_append_data_request_builder_multiple_calls() {
357        let mut builder = AppendDataRequest::builder();
358
359        // Test that multiple calls override previous values
360        builder = builder.spreadsheet_token("first_token");
361        builder = builder.spreadsheet_token("second_token");
362        builder = builder.range("first_range");
363        builder = builder.range("second_range");
364
365        let request = builder.build();
366
367        assert_eq!(request.spreadsheet_token, "second_token");
368        assert_eq!(request.value_range.range, "second_range");
369    }
370
371    #[test]
372    fn test_spreadsheet_sheet_service_creation() {
373        let service = create_test_service();
374
375        // Verify the service can be created without panic
376        assert_eq!(service.config.app_id, "test_app_id");
377    }
378
379    #[test]
380    fn test_append_data_response_type_alias() {
381        // Verify that AppendDataResponse is properly aliased
382        let _response: AppendDataResponse = UpdateSheetDataResponse {
383            spreed_sheet_token: "test".to_string(),
384            table_range: "A1:B2".to_string(),
385            revision: 1,
386            updates: crate::service::cloud_docs::sheets::v2::data_operation::SheetDataUpdates {
387                spreed_sheet_token: "test".to_string(),
388                updated_range: "A1:B2".to_string(),
389                updated_rows: 2,
390                updated_columns: 2,
391                updated_cells: 4,
392                revision: Some(1),
393            },
394        };
395    }
396
397    #[test]
398    fn test_append_data_request_edge_cases() {
399        // Test with very long token
400        let long_token = "a".repeat(10000);
401        let request = AppendDataRequest::builder()
402            .spreadsheet_token(&long_token)
403            .build();
404        assert_eq!(request.spreadsheet_token, long_token);
405
406        // Test with empty array values
407        let empty_array = json!([]);
408        let request = AppendDataRequest::builder()
409            .values(empty_array.clone())
410            .build();
411        assert_eq!(request.value_range.values, empty_array);
412
413        // Test with nested array values
414        let nested_array = json!([[[["deeply", "nested"], ["values", "here"]]]]);
415        let request = AppendDataRequest::builder()
416            .values(nested_array.clone())
417            .build();
418        assert_eq!(request.value_range.values, nested_array);
419    }
420
421    #[test]
422    fn test_value_range_request_default() {
423        let value_range = ValueRangeRequest::default();
424        assert_eq!(value_range.range, "");
425        assert_eq!(value_range.values, Value::Null);
426    }
427
428    #[test]
429    fn test_value_range_request_serialization() {
430        let value_range = ValueRangeRequest {
431            range: "A1:B2".to_string(),
432            values: json!([["1", "2"]]),
433        };
434
435        let serialized = serde_json::to_string(&value_range);
436        assert!(serialized.is_ok());
437
438        let json_str = serialized.unwrap();
439        assert!(json_str.contains("range"));
440        assert!(json_str.contains("values"));
441        assert!(json_str.contains("A1:B2"));
442    }
443
444    #[test]
445    fn test_append_data_request_memory_efficiency() {
446        // Test creating many requests doesn't consume excessive memory
447        let requests: Vec<AppendDataRequest> = (0..100)
448            .map(|i| {
449                AppendDataRequest::builder()
450                    .spreadsheet_token(format!("token_{}", i))
451                    .range(format!("Sheet{}!A:B", i))
452                    .values(json!([[i.to_string(), (i + 1).to_string()]]))
453                    .build()
454            })
455            .collect();
456
457        assert_eq!(requests.len(), 100);
458
459        // Verify each request has correct data
460        for (i, request) in requests.iter().enumerate() {
461            assert_eq!(request.spreadsheet_token, format!("token_{}", i));
462            assert_eq!(request.value_range.range, format!("Sheet{}!A:B", i));
463        }
464    }
465}