use openlark_core::{
api::{ApiRequest, ApiResponseTrait, ResponseFormat},
config::Config,
http::Transport,
SDKResult,
};
use serde::{Deserialize, Serialize};
use crate::common::{api_endpoints::DriveApi, api_utils::*};
#[derive(Debug)]
pub struct UploadAllMediaRequest {
config: Config,
pub file_name: String,
pub parent_type: String,
pub parent_node: String,
pub size: usize,
pub checksum: Option<String>,
pub extra: Option<String>,
pub file: Vec<u8>,
}
impl UploadAllMediaRequest {
pub fn new(
config: Config,
file_name: impl Into<String>,
parent_type: impl Into<String>,
parent_node: impl Into<String>,
size: usize,
file: Vec<u8>,
) -> Self {
Self {
config,
file_name: file_name.into(),
parent_type: parent_type.into(),
parent_node: parent_node.into(),
size,
checksum: None,
extra: None,
file,
}
}
pub fn checksum(mut self, checksum: impl Into<String>) -> Self {
self.checksum = Some(checksum.into());
self
}
pub fn extra(mut self, extra: impl Into<String>) -> Self {
self.extra = Some(extra.into());
self
}
pub async fn execute(self) -> SDKResult<UploadAllMediaResponse> {
self.execute_with_options(openlark_core::req_option::RequestOption::default())
.await
}
pub async fn execute_with_options(
self,
option: openlark_core::req_option::RequestOption,
) -> SDKResult<UploadAllMediaResponse> {
let file_name_len = self.file_name.chars().count();
if file_name_len == 0 || file_name_len > 250 {
return Err(openlark_core::error::validation_error(
"file_name",
"file_name 长度必须在 1~250 字符之间",
));
}
if self.parent_node.is_empty() {
return Err(openlark_core::error::validation_error(
"parent_node",
"parent_node 不能为空",
));
}
match self.parent_type.as_str() {
"doc_image"
| "docx_image"
| "sheet_image"
| "doc_file"
| "docx_file"
| "sheet_file"
| "vc_virtual_background"
| "bitable_image"
| "bitable_file"
| "moments"
| "ccm_import_open" => {}
_ => {
return Err(openlark_core::error::validation_error(
"parent_type",
"parent_type 不在支持的取值范围内",
))
}
}
if self.size == 0 || self.size > 20 * 1024 * 1024 {
return Err(openlark_core::error::validation_error(
"size",
"size 必须在 1~20971520 字节之间",
));
}
if self.file.len() != self.size {
return Err(openlark_core::error::validation_error(
"size",
"size 必须与 file 的实际长度一致",
));
}
let api_endpoint = DriveApi::UploadMedia;
#[derive(Serialize)]
struct UploadMeta {
file_name: String,
parent_type: String,
parent_node: String,
size: usize,
#[serde(skip_serializing_if = "Option::is_none")]
checksum: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
extra: Option<String>,
}
let meta = UploadMeta {
file_name: self.file_name,
parent_type: self.parent_type,
parent_node: self.parent_node,
size: self.size,
checksum: self.checksum,
extra: self.extra,
};
let request = ApiRequest::<UploadAllMediaResponse>::post(&api_endpoint.to_url())
.json_body(&meta)
.file_content(self.file);
let response = Transport::request(request, &self.config, Some(option)).await?;
extract_response_data(response, "上传")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UploadAllMediaResponse {
pub file_token: String,
}
impl ApiResponseTrait for UploadAllMediaResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[cfg(test)]
mod tests {
use super::*;
use openlark_core::testing::prelude::test_runtime;
#[test]
fn test_upload_file_request_builder() {
let config = Config::default();
let request = UploadAllMediaRequest::new(
config,
"test.png",
"docx_image",
"doc_token",
100,
vec![0; 100],
)
.extra("extra_info");
assert_eq!(request.file_name, "test.png");
assert_eq!(request.parent_type, "docx_image");
assert_eq!(request.extra, Some("extra_info".to_string()));
}
#[test]
fn test_empty_file_name() {
let config = Config::default();
let request =
UploadAllMediaRequest::new(config, "", "docx_image", "doc_token", 100, vec![0; 100]);
let rt = test_runtime();
let result = rt.block_on(request.execute());
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("file_name"));
}
#[test]
fn test_file_name_too_long() {
let config = Config::default();
let long_name = "a".repeat(251);
let request = UploadAllMediaRequest::new(
config,
long_name,
"docx_image",
"doc_token",
100,
vec![0; 100],
);
let rt = test_runtime();
let result = rt.block_on(request.execute());
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("250"));
}
#[test]
fn test_empty_parent_node() {
let config = Config::default();
let request =
UploadAllMediaRequest::new(config, "test.png", "docx_image", "", 100, vec![0; 100]);
let rt = test_runtime();
let result = rt.block_on(request.execute());
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("parent_node"));
}
#[test]
fn test_invalid_parent_type() {
let config = Config::default();
let request = UploadAllMediaRequest::new(
config,
"test.png",
"invalid_type",
"doc_token",
100,
vec![0; 100],
);
let rt = test_runtime();
let result = rt.block_on(request.execute());
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("parent_type"));
}
#[test]
fn test_invalid_size_zero() {
let config = Config::default();
let request =
UploadAllMediaRequest::new(config, "test.png", "docx_image", "doc_token", 0, vec![]);
let rt = test_runtime();
let result = rt.block_on(request.execute());
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("size"));
}
#[test]
fn test_invalid_size_too_large() {
let config = Config::default();
let request = UploadAllMediaRequest::new(
config,
"test.png",
"docx_image",
"doc_token",
20 * 1024 * 1024 + 1,
vec![0; 20 * 1024 * 1024 + 1],
);
let rt = test_runtime();
let result = rt.block_on(request.execute());
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("20971520"));
}
#[test]
fn test_size_mismatch() {
let config = Config::default();
let request = UploadAllMediaRequest::new(
config,
"test.png",
"docx_image",
"doc_token",
100,
vec![0; 50],
);
let rt = test_runtime();
let result = rt.block_on(request.execute());
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("一致"));
}
}