open_lark/service/cloud_docs/bitable/v1/app/
create.rs1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 core::{
6 api_req::ApiRequest,
7 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
8 constants::AccessTokenType,
9 endpoints::cloud_docs::*,
10 http::Transport,
11 req_option::RequestOption,
12 SDKResult,
13 },
14 impl_executable_builder_owned,
15};
16
17use super::AppService;
18
19impl AppService {
20 pub async fn create(
24 &self,
25 request: CreateAppRequest,
26 option: Option<RequestOption>,
27 ) -> SDKResult<BaseResponse<CreateAppResponse>> {
28 let mut api_req = request.api_request;
29 api_req.http_method = Method::POST;
30 api_req.api_path = BITABLE_V1_APPS.to_string();
31 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
32 api_req.body = serde_json::to_vec(&CreateAppRequestBody {
33 name: request.name,
34 folder_token: request.folder_token,
35 time_zone: request.time_zone,
36 })?;
37
38 let api_resp = Transport::request(api_req, &self.config, option).await?;
39 Ok(api_resp)
40 }
41}
42
43#[derive(Debug, Default)]
45pub struct CreateAppRequest {
46 api_request: ApiRequest,
47 name: String,
49 folder_token: Option<String>,
51 time_zone: Option<String>,
53}
54
55impl CreateAppRequest {
56 pub fn builder() -> CreateAppRequestBuilder {
57 CreateAppRequestBuilder::default()
58 }
59}
60
61#[derive(Default)]
62pub struct CreateAppRequestBuilder {
63 request: CreateAppRequest,
64}
65
66impl CreateAppRequestBuilder {
67 pub fn name(mut self, name: impl ToString) -> Self {
69 self.request.name = name.to_string();
70 self
71 }
72
73 pub fn folder_token(mut self, folder_token: impl ToString) -> Self {
75 self.request.folder_token = Some(folder_token.to_string());
76 self
77 }
78
79 pub fn time_zone(mut self, time_zone: impl ToString) -> Self {
81 self.request.time_zone = Some(time_zone.to_string());
82 self
83 }
84
85 pub fn build(self) -> CreateAppRequest {
86 self.request
87 }
88}
89
90impl_executable_builder_owned!(
91 CreateAppRequestBuilder,
92 AppService,
93 CreateAppRequest,
94 BaseResponse<CreateAppResponse>,
95 create
96);
97
98#[derive(Serialize)]
99struct CreateAppRequestBody {
100 name: String,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 folder_token: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 time_zone: Option<String>,
105}
106
107#[derive(Deserialize, Debug)]
108pub struct CreateAppResponse {
109 pub app: CreateAppResponseData,
111}
112
113#[derive(Deserialize, Debug)]
114pub struct CreateAppResponseData {
115 pub app_token: String,
117 pub name: String,
119 pub revision: i32,
121 pub url: String,
123}
124
125impl ApiResponseTrait for CreateAppResponse {
126 fn data_format() -> ResponseFormat {
127 ResponseFormat::Data
128 }
129}
130
131#[cfg(test)]
132#[allow(unused_variables, unused_unsafe)]
133mod tests {
134 use super::*;
135 use serde_json::json;
136
137 #[test]
138 fn test_create_app_request() {
139 let request = CreateAppRequest::builder()
140 .name("测试多维表格")
141 .folder_token("fldcnmBA*****yGehy8")
142 .time_zone("Asia/Shanghai")
143 .build();
144
145 assert_eq!(request.name, "测试多维表格");
146 assert_eq!(
147 request.folder_token,
148 Some("fldcnmBA*****yGehy8".to_string())
149 );
150 assert_eq!(request.time_zone, Some("Asia/Shanghai".to_string()));
151 }
152
153 #[test]
154 fn test_create_app_request_body_serialization() {
155 let body = CreateAppRequestBody {
156 name: "测试多维表格".to_string(),
157 folder_token: Some("fldcnmBA*****yGehy8".to_string()),
158 time_zone: Some("Asia/Shanghai".to_string()),
159 };
160
161 let serialized = serde_json::to_value(&body).unwrap();
162 let expected = json!({
163 "name": "测试多维表格",
164 "folder_token": "fldcnmBA*****yGehy8",
165 "time_zone": "Asia/Shanghai"
166 });
167
168 assert_eq!(serialized, expected);
169 }
170
171 #[test]
172 fn test_create_app_request_builder_default() {
173 let builder = CreateAppRequestBuilder::default();
174 let request = builder.build();
175
176 assert_eq!(request.name, "");
177 assert_eq!(request.folder_token, None);
178 assert_eq!(request.time_zone, None);
179 }
180
181 #[test]
182 fn test_create_app_request_default() {
183 let request = CreateAppRequest::default();
184
185 assert_eq!(request.name, "");
186 assert_eq!(request.folder_token, None);
187 assert_eq!(request.time_zone, None);
188 }
189
190 #[test]
191 fn test_create_app_request_minimal() {
192 let request = CreateAppRequest::builder().name("简单表格").build();
193
194 assert_eq!(request.name, "简单表格");
195 assert_eq!(request.folder_token, None);
196 assert_eq!(request.time_zone, None);
197 }
198
199 #[test]
200 fn test_create_app_request_with_folder_only() {
201 let request = CreateAppRequest::builder()
202 .name("文件夹表格")
203 .folder_token("folder123")
204 .build();
205
206 assert_eq!(request.name, "文件夹表格");
207 assert_eq!(request.folder_token, Some("folder123".to_string()));
208 assert_eq!(request.time_zone, None);
209 }
210
211 #[test]
212 fn test_create_app_request_with_timezone_only() {
213 let request = CreateAppRequest::builder()
214 .name("时区表格")
215 .time_zone("UTC")
216 .build();
217
218 assert_eq!(request.name, "时区表格");
219 assert_eq!(request.folder_token, None);
220 assert_eq!(request.time_zone, Some("UTC".to_string()));
221 }
222
223 #[test]
224 fn test_create_app_request_builder_chaining() {
225 let request = CreateAppRequest::builder()
226 .name("链式调用")
227 .folder_token("folder456")
228 .time_zone("Europe/London")
229 .name("更新名称")
230 .build();
231
232 assert_eq!(request.name, "更新名称");
233 assert_eq!(request.folder_token, Some("folder456".to_string()));
234 assert_eq!(request.time_zone, Some("Europe/London".to_string()));
235 }
236
237 #[test]
238 fn test_create_app_request_debug() {
239 let request = CreateAppRequest::builder().name("调试测试").build();
240
241 let debug_str = format!("{:?}", request);
242 assert!(debug_str.contains("CreateAppRequest"));
243 assert!(debug_str.contains("调试测试"));
244 }
245
246 #[test]
247 fn test_create_app_request_with_unicode_name() {
248 let unicode_name = "测试表格🚀📊📈";
249 let request = CreateAppRequest::builder().name(unicode_name).build();
250
251 assert_eq!(request.name, unicode_name);
252 }
253
254 #[test]
255 fn test_create_app_request_with_string_types() {
256 let owned_string = String::from("拥有字符串");
257 let request1 = CreateAppRequest::builder().name(owned_string).build();
258 assert_eq!(request1.name, "拥有字符串");
259
260 let string_ref = "引用字符串";
261 let request2 = CreateAppRequest::builder().name(string_ref).build();
262 assert_eq!(request2.name, "引用字符串");
263 }
264
265 #[test]
266 fn test_create_app_request_body_with_none_values() {
267 let body = CreateAppRequestBody {
268 name: "基础表格".to_string(),
269 folder_token: None,
270 time_zone: None,
271 };
272
273 let serialized = serde_json::to_value(&body).unwrap();
274 let expected = json!({
275 "name": "基础表格"
276 });
277
278 assert_eq!(serialized, expected);
279 }
280
281 #[test]
282 fn test_create_app_request_body_with_empty_strings() {
283 let body = CreateAppRequestBody {
284 name: "".to_string(),
285 folder_token: Some("".to_string()),
286 time_zone: Some("".to_string()),
287 };
288
289 let serialized = serde_json::to_value(&body).unwrap();
290 let expected = json!({
291 "name": "",
292 "folder_token": "",
293 "time_zone": ""
294 });
295
296 assert_eq!(serialized, expected);
297 }
298
299 #[test]
300 fn test_create_app_response_deserialization() {
301 let json = r#"{
302 "app": {
303 "app_token": "bascnmBA*****yGehy8",
304 "name": "新建多维表格",
305 "revision": 1,
306 "url": "https://example.feishu.cn/base/bascnmBA*****yGehy8"
307 }
308 }"#;
309
310 let response: CreateAppResponse = serde_json::from_str(json).unwrap();
311 assert_eq!(response.app.app_token, "bascnmBA*****yGehy8");
312 assert_eq!(response.app.name, "新建多维表格");
313 assert_eq!(response.app.revision, 1);
314 assert_eq!(
315 response.app.url,
316 "https://example.feishu.cn/base/bascnmBA*****yGehy8"
317 );
318 }
319
320 #[test]
321 fn test_create_app_response_data_debug() {
322 let app_data = CreateAppResponseData {
323 app_token: "test_token".to_string(),
324 name: "Test App".to_string(),
325 revision: 1,
326 url: "https://test.url".to_string(),
327 };
328
329 let debug_str = format!("{:?}", app_data);
330 assert!(debug_str.contains("CreateAppResponseData"));
331 assert!(debug_str.contains("test_token"));
332 assert!(debug_str.contains("Test App"));
333 assert!(debug_str.contains("https://test.url"));
334 }
335
336 #[test]
337 fn test_create_app_response_debug() {
338 let response = CreateAppResponse {
339 app: CreateAppResponseData {
340 app_token: "debug_token".to_string(),
341 name: "Debug App".to_string(),
342 revision: 2,
343 url: "https://debug.url".to_string(),
344 },
345 };
346
347 let debug_str = format!("{:?}", response);
348 assert!(debug_str.contains("CreateAppResponse"));
349 assert!(debug_str.contains("debug_token"));
350 assert!(debug_str.contains("Debug App"));
351 }
352
353 #[test]
354 fn test_create_app_response_data_format() {
355 let format = CreateAppResponse::data_format();
356 assert!(matches!(format, ResponseFormat::Data));
357 }
358
359 #[test]
360 fn test_create_app_response_with_different_revisions() {
361 let revisions = vec![0, 1, 5, 100, 999999];
362
363 for revision in revisions {
364 let json = format!(
365 r#"{{
366 "app": {{
367 "app_token": "test_token",
368 "name": "Test App",
369 "revision": {},
370 "url": "https://test.url"
371 }}
372 }}"#,
373 revision
374 );
375
376 let response: CreateAppResponse = serde_json::from_str(&json).unwrap();
377 assert_eq!(response.app.revision, revision);
378 }
379 }
380
381 #[test]
382 fn test_create_app_response_with_unicode_data() {
383 let json = r#"{
384 "app": {
385 "app_token": "unicode_token",
386 "name": "多维表格📊数据分析🔍",
387 "revision": 1,
388 "url": "https://飞书.cn/base/unicode_token"
389 }
390 }"#;
391
392 let response: CreateAppResponse = serde_json::from_str(json).unwrap();
393 assert_eq!(response.app.name, "多维表格📊数据分析🔍");
394 assert_eq!(response.app.url, "https://飞书.cn/base/unicode_token");
395 }
396
397 #[test]
398 fn test_create_app_request_body_various_timezones() {
399 let timezones = vec![
400 "UTC",
401 "Asia/Shanghai",
402 "America/New_York",
403 "Europe/London",
404 "Asia/Tokyo",
405 "Australia/Sydney",
406 ];
407
408 for tz in timezones {
409 let body = CreateAppRequestBody {
410 name: "时区测试".to_string(),
411 folder_token: None,
412 time_zone: Some(tz.to_string()),
413 };
414
415 let serialized = serde_json::to_value(&body).unwrap();
416 let expected = json!({
417 "name": "时区测试",
418 "time_zone": tz
419 });
420
421 assert_eq!(serialized, expected);
422 }
423 }
424
425 #[test]
426 fn test_memory_efficiency() {
427 let request = CreateAppRequest::builder().name("内存测试").build();
428
429 let size = std::mem::size_of_val(&request);
430 assert!(size > 0);
431 assert!(size < 1024);
432 }
433
434 #[test]
435 fn test_create_app_request_with_long_name() {
436 let long_name = "a".repeat(1000);
437 let request = CreateAppRequest::builder().name(&long_name).build();
438
439 assert_eq!(request.name, long_name);
440 }
441
442 #[test]
443 fn test_create_app_request_builder_method_returns() {
444 let builder = CreateAppRequest::builder().name("测试链式");
445
446 let _chained = builder.folder_token("folder").time_zone("UTC");
448 }
449}