use std::sync::Arc;
use openlark_core::error::api_error;
#[derive(Debug)]
pub struct OpenApiLogsService {
config: Arc<crate::models::SecurityConfig>,
}
impl OpenApiLogsService {
pub fn new(config: Arc<crate::models::SecurityConfig>) -> Self {
Self { config }
}
pub fn list_data(&self) -> ListOpenApiLogsBuilder {
ListOpenApiLogsBuilder {
config: self.config.clone(),
start_time: None,
end_time: None,
user_id_filter: None,
api_path_filter: None,
status_code_filter: None,
app_id_filter: None,
page_size: Some(20),
page_token: None,
sort_field: Some("request_time".to_string()),
sort_direction: Some("desc".to_string()),
}
}
}
#[derive(Debug)]
pub struct ListOpenApiLogsBuilder {
config: Arc<crate::models::SecurityConfig>,
start_time: Option<crate::models::Timestamp>,
end_time: Option<crate::models::Timestamp>,
user_id_filter: Option<String>,
api_path_filter: Option<String>,
status_code_filter: Option<i32>,
app_id_filter: Option<String>,
page_size: Option<i32>,
page_token: Option<String>,
sort_field: Option<String>,
sort_direction: Option<String>,
}
impl ListOpenApiLogsBuilder {
pub fn start_time(mut self, start_time: crate::models::Timestamp) -> Self {
self.start_time = Some(start_time);
self
}
pub fn end_time(mut self, end_time: crate::models::Timestamp) -> Self {
self.end_time = Some(end_time);
self
}
pub fn time_range(
mut self,
start_time: crate::models::Timestamp,
end_time: crate::models::Timestamp,
) -> Self {
self.start_time = Some(start_time);
self.end_time = Some(end_time);
self
}
pub fn last_days(mut self, days: i64) -> Self {
use chrono::Utc;
let now = Utc::now().timestamp();
let start_time = now - days * 24 * 3600;
self.start_time = Some(start_time);
self.end_time = Some(now);
self
}
pub fn last_hours(mut self, hours: i64) -> Self {
use chrono::Utc;
let now = Utc::now().timestamp();
let start_time = now - hours * 3600;
self.start_time = Some(start_time);
self.end_time = Some(now);
self
}
pub fn user_id_filter(mut self, user_id: impl Into<String>) -> Self {
self.user_id_filter = Some(user_id.into());
self
}
pub fn api_path_filter(mut self, api_path: impl Into<String>) -> Self {
self.api_path_filter = Some(api_path.into());
self
}
pub fn api_path_contains(mut self, pattern: impl Into<String>) -> Self {
self.api_path_filter = Some(format!("*{}*", pattern.into()));
self
}
pub fn status_code_filter(mut self, status_code: i32) -> Self {
self.status_code_filter = Some(status_code);
self
}
pub fn app_id_filter(mut self, app_id: impl Into<String>) -> Self {
self.app_id_filter = Some(app_id.into());
self
}
pub fn page_size(mut self, page_size: i32) -> Self {
self.page_size = Some(page_size);
self
}
pub fn page_token(mut self, page_token: impl Into<String>) -> Self {
self.page_token = Some(page_token.into());
self
}
pub fn sort_field(mut self, sort_field: impl Into<String>) -> Self {
self.sort_field = Some(sort_field.into());
self
}
pub fn sort_direction(mut self, sort_direction: impl Into<String>) -> Self {
self.sort_direction = Some(sort_direction.into());
self
}
pub fn sort_by_time_asc(mut self) -> Self {
self.sort_field = Some("request_time".to_string());
self.sort_direction = Some("asc".to_string());
self
}
pub fn sort_by_time_desc(mut self) -> Self {
self.sort_field = Some("request_time".to_string());
self.sort_direction = Some("desc".to_string());
self
}
pub async fn send(
self,
) -> crate::SecurityResult<
crate::models::PageResponse<crate::models::security_and_compliance::OpenApiLog>,
> {
let url = format!(
"{}/open-apis/security_and_compliance/v1/openapi_logs/list_data",
self.config.base_url
);
let mut request_body = serde_json::Map::new();
if let Some(start_time) = self.start_time {
request_body.insert(
"start_time".to_string(),
serde_json::Value::Number(start_time.into()),
);
}
if let Some(end_time) = self.end_time {
request_body.insert(
"end_time".to_string(),
serde_json::Value::Number(end_time.into()),
);
}
if let Some(user_id_filter) = &self.user_id_filter {
request_body.insert(
"user_id_filter".to_string(),
serde_json::Value::String(user_id_filter.clone()),
);
}
if let Some(api_path_filter) = &self.api_path_filter {
request_body.insert(
"api_path_filter".to_string(),
serde_json::Value::String(api_path_filter.clone()),
);
}
if let Some(status_code_filter) = self.status_code_filter {
request_body.insert(
"status_code_filter".to_string(),
serde_json::Value::Number(status_code_filter.into()),
);
}
if let Some(app_id_filter) = &self.app_id_filter {
request_body.insert(
"app_id_filter".to_string(),
serde_json::Value::String(app_id_filter.clone()),
);
}
if let Some(page_size) = self.page_size {
request_body.insert(
"page_size".to_string(),
serde_json::Value::Number(page_size.into()),
);
}
if let Some(page_token) = &self.page_token {
request_body.insert(
"page_token".to_string(),
serde_json::Value::String(page_token.clone()),
);
}
if let Some(sort_field) = &self.sort_field {
request_body.insert(
"sort_field".to_string(),
serde_json::Value::String(sort_field.clone()),
);
}
if let Some(sort_direction) = &self.sort_direction {
request_body.insert(
"sort_direction".to_string(),
serde_json::Value::String(sort_direction.clone()),
);
}
let response = reqwest::Client::new()
.post(&url)
.header(
"Authorization",
format!("Bearer {}", get_app_token(&self.config).await?),
)
.header("Content-Type", "application/json")
.json(&request_body)
.send()
.await?;
if response.status().is_success() {
let api_response: crate::models::ApiResponse<
crate::models::PageResponse<crate::models::security_and_compliance::OpenApiLog>,
> = response.json().await?;
match api_response.data {
Some(logs) => Ok(logs),
None => Err(api_error(
api_response.code as u16,
"/security_and_compliance/v1/openapi_logs",
&api_response.msg,
None,
)),
}
} else {
Err(api_error(
response.status().as_u16(),
"/security_and_compliance/v1/openapi_logs",
format!("HTTP: {}", response.status()),
None,
))
}
}
}
async fn get_app_token(config: &crate::models::SecurityConfig) -> crate::SecurityResult<String> {
config.get_app_access_token().await
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_config() -> Arc<crate::models::SecurityConfig> {
Arc::new(crate::models::SecurityConfig {
app_id: "test_app_id".to_string(),
app_secret: "test_app_secret".to_string(),
base_url: "https://open.feishu.cn".to_string(),
})
}
#[test]
fn test_openapi_logs_service_creation() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
assert_eq!(service.config.app_id, "test_app_id");
}
#[test]
fn test_list_openapi_logs_builder_defaults() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service.list_data();
assert_eq!(builder.page_size, Some(20));
assert_eq!(builder.page_token, None);
assert_eq!(builder.start_time, None);
assert_eq!(builder.end_time, None);
assert_eq!(builder.sort_field, Some("request_time".to_string()));
assert_eq!(builder.sort_direction, Some("desc".to_string()));
}
#[test]
fn test_list_openapi_logs_builder_with_filters() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service
.list_data()
.user_id_filter("user_123")
.api_path_filter("/open-apis/contact/v3/users")
.status_code_filter(200)
.app_id_filter("cli_123456")
.page_size(50)
.page_token("token_abc");
assert_eq!(builder.user_id_filter, Some("user_123".to_string()));
assert_eq!(
builder.api_path_filter,
Some("/open-apis/contact/v3/users".to_string())
);
assert_eq!(builder.status_code_filter, Some(200));
assert_eq!(builder.app_id_filter, Some("cli_123456".to_string()));
assert_eq!(builder.page_size, Some(50));
assert_eq!(builder.page_token, Some("token_abc".to_string()));
}
#[test]
fn test_list_openapi_logs_builder_time_range() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service.list_data().time_range(1000000, 2000000);
assert_eq!(builder.start_time, Some(1000000));
assert_eq!(builder.end_time, Some(2000000));
}
#[test]
fn test_list_openapi_logs_builder_last_days() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service.list_data().last_days(7);
assert!(builder.start_time.is_some());
assert!(builder.end_time.is_some());
assert!(builder.end_time.unwrap() > builder.start_time.unwrap());
}
#[test]
fn test_list_openapi_logs_builder_last_hours() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service.list_data().last_hours(24);
assert!(builder.start_time.is_some());
assert!(builder.end_time.is_some());
assert!(builder.end_time.unwrap() > builder.start_time.unwrap());
}
#[test]
fn test_list_openapi_logs_builder_api_path_contains() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service.list_data().api_path_contains("users");
assert_eq!(builder.api_path_filter, Some("*users*".to_string()));
}
#[test]
fn test_list_openapi_logs_sort_methods() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder_asc = service.list_data().sort_by_time_asc();
assert_eq!(builder_asc.sort_field, Some("request_time".to_string()));
assert_eq!(builder_asc.sort_direction, Some("asc".to_string()));
let builder_desc = service.list_data().sort_by_time_desc();
assert_eq!(builder_desc.sort_field, Some("request_time".to_string()));
assert_eq!(builder_desc.sort_direction, Some("desc".to_string()));
}
#[test]
fn test_list_openapi_logs_builder_chaining() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service
.list_data()
.last_days(30)
.user_id_filter("user_456")
.status_code_filter(500)
.page_size(100)
.sort_by_time_desc();
assert!(builder.start_time.is_some());
assert!(builder.end_time.is_some());
assert_eq!(builder.user_id_filter, Some("user_456".to_string()));
assert_eq!(builder.status_code_filter, Some(500));
assert_eq!(builder.page_size, Some(100));
assert_eq!(builder.sort_direction, Some("desc".to_string()));
}
#[test]
fn test_list_openapi_logs_status_code_variants() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder_200 = service.list_data().status_code_filter(200);
assert_eq!(builder_200.status_code_filter, Some(200));
let builder_400 = service.list_data().status_code_filter(400);
assert_eq!(builder_400.status_code_filter, Some(400));
let builder_500 = service.list_data().status_code_filter(500);
assert_eq!(builder_500.status_code_filter, Some(500));
}
#[test]
fn test_list_openapi_logs_custom_sort() {
let config = create_test_config();
let service = OpenApiLogsService::new(config);
let builder = service
.list_data()
.sort_field("user_id")
.sort_direction("asc");
assert_eq!(builder.sort_field, Some("user_id".to_string()));
assert_eq!(builder.sort_direction, Some("asc".to_string()));
}
}