fix: resolve clippy format-args failures and OpenSSL vendoring issue
Inline format arguments across Rust modules to satisfy clippy -D warnings, and configure Cargo to prefer system OpenSSL so clippy builds do not fail on missing vendored Perl modules. Made-with: Cursor
This commit is contained in:
parent
bdb63f3aee
commit
85a8d0a4c0
3
.cargo/config.toml
Normal file
3
.cargo/config.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[env]
|
||||||
|
# Force use of system OpenSSL instead of vendored OpenSSL source builds.
|
||||||
|
OPENSSL_NO_VENDOR = "1"
|
||||||
@ -4,3 +4,8 @@
|
|||||||
# error. The desktop binary links against rlib (static), so cdylib exports
|
# error. The desktop binary links against rlib (static), so cdylib exports
|
||||||
# are unused at runtime.
|
# are unused at runtime.
|
||||||
rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"]
|
rustflags = ["-C", "link-arg=-Wl,--exclude-all-symbols"]
|
||||||
|
|
||||||
|
[env]
|
||||||
|
# Use system OpenSSL instead of vendoring from source (which requires Perl modules
|
||||||
|
# unavailable on some environments and breaks clippy/check).
|
||||||
|
OPENSSL_NO_VENDOR = "1"
|
||||||
|
|||||||
@ -47,7 +47,10 @@ impl Provider for MistralProvider {
|
|||||||
|
|
||||||
let resp = client
|
let resp = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
.header("Authorization", format!("Bearer {}", config.api_key))
|
.header(
|
||||||
|
"Authorization",
|
||||||
|
format!("Bearer {api_key}", api_key = config.api_key),
|
||||||
|
)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.json(&body)
|
.json(&body)
|
||||||
.send()
|
.send()
|
||||||
|
|||||||
@ -54,7 +54,8 @@ impl OpenAiProvider {
|
|||||||
.custom_endpoint_path
|
.custom_endpoint_path
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or("/chat/completions");
|
.unwrap_or("/chat/completions");
|
||||||
let url = format!("{}{}", config.api_url.trim_end_matches('/'), endpoint_path);
|
let api_url = config.api_url.trim_end_matches('/');
|
||||||
|
let url = format!("{api_url}{endpoint_path}");
|
||||||
|
|
||||||
let mut body = serde_json::json!({
|
let mut body = serde_json::json!({
|
||||||
"model": config.model,
|
"model": config.model,
|
||||||
@ -75,7 +76,7 @@ impl OpenAiProvider {
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or("Authorization");
|
.unwrap_or("Authorization");
|
||||||
let auth_prefix = config.custom_auth_prefix.as_deref().unwrap_or("Bearer ");
|
let auth_prefix = config.custom_auth_prefix.as_deref().unwrap_or("Bearer ");
|
||||||
let auth_value = format!("{}{}", auth_prefix, config.api_key);
|
let auth_value = format!("{auth_prefix}{api_key}", api_key = config.api_key);
|
||||||
|
|
||||||
let resp = client
|
let resp = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
@ -122,7 +123,8 @@ impl OpenAiProvider {
|
|||||||
|
|
||||||
// Use custom endpoint path, default to empty (API URL already includes /api/v2/chat)
|
// Use custom endpoint path, default to empty (API URL already includes /api/v2/chat)
|
||||||
let endpoint_path = config.custom_endpoint_path.as_deref().unwrap_or("");
|
let endpoint_path = config.custom_endpoint_path.as_deref().unwrap_or("");
|
||||||
let url = format!("{}{}", config.api_url.trim_end_matches('/'), endpoint_path);
|
let api_url = config.api_url.trim_end_matches('/');
|
||||||
|
let url = format!("{api_url}{endpoint_path}");
|
||||||
|
|
||||||
// Extract system message if present
|
// Extract system message if present
|
||||||
let system_message = messages
|
let system_message = messages
|
||||||
@ -177,7 +179,7 @@ impl OpenAiProvider {
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or("x-msi-genai-api-key");
|
.unwrap_or("x-msi-genai-api-key");
|
||||||
let auth_prefix = config.custom_auth_prefix.as_deref().unwrap_or("");
|
let auth_prefix = config.custom_auth_prefix.as_deref().unwrap_or("");
|
||||||
let auth_value = format!("{}{}", auth_prefix, config.api_key);
|
let auth_value = format!("{auth_prefix}{api_key}", api_key = config.api_key);
|
||||||
|
|
||||||
let resp = client
|
let resp = client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
|
|||||||
@ -246,7 +246,7 @@ pub async fn chat_message(
|
|||||||
"api_url": provider_config.api_url,
|
"api_url": provider_config.api_url,
|
||||||
"user_message": user_msg.content,
|
"user_message": user_msg.content,
|
||||||
"response_preview": if response.content.len() > 200 {
|
"response_preview": if response.content.len() > 200 {
|
||||||
format!("{}...", &response.content[..200])
|
format!("{preview}...", preview = &response.content[..200])
|
||||||
} else {
|
} else {
|
||||||
response.content.clone()
|
response.content.clone()
|
||||||
},
|
},
|
||||||
|
|||||||
@ -295,19 +295,19 @@ pub async fn list_issues(
|
|||||||
let mut params: Vec<Box<dyn rusqlite::types::ToSql>> = vec![];
|
let mut params: Vec<Box<dyn rusqlite::types::ToSql>> = vec![];
|
||||||
|
|
||||||
if let Some(ref status) = filter.status {
|
if let Some(ref status) = filter.status {
|
||||||
sql.push_str(&format!(" AND i.status = ?{}", params.len() + 1));
|
sql.push_str(&format!(" AND i.status = ?{index}", index = params.len() + 1));
|
||||||
params.push(Box::new(status.clone()));
|
params.push(Box::new(status.clone()));
|
||||||
}
|
}
|
||||||
if let Some(ref severity) = filter.severity {
|
if let Some(ref severity) = filter.severity {
|
||||||
sql.push_str(&format!(" AND i.severity = ?{}", params.len() + 1));
|
sql.push_str(&format!(" AND i.severity = ?{index}", index = params.len() + 1));
|
||||||
params.push(Box::new(severity.clone()));
|
params.push(Box::new(severity.clone()));
|
||||||
}
|
}
|
||||||
if let Some(ref category) = filter.category {
|
if let Some(ref category) = filter.category {
|
||||||
sql.push_str(&format!(" AND i.category = ?{}", params.len() + 1));
|
sql.push_str(&format!(" AND i.category = ?{index}", index = params.len() + 1));
|
||||||
params.push(Box::new(category.clone()));
|
params.push(Box::new(category.clone()));
|
||||||
}
|
}
|
||||||
if let Some(ref domain) = filter.domain {
|
if let Some(ref domain) = filter.domain {
|
||||||
sql.push_str(&format!(" AND i.category = ?{}", params.len() + 1));
|
sql.push_str(&format!(" AND i.category = ?{index}", index = params.len() + 1));
|
||||||
params.push(Box::new(domain.clone()));
|
params.push(Box::new(domain.clone()));
|
||||||
}
|
}
|
||||||
if let Some(ref search) = filter.search {
|
if let Some(ref search) = filter.search {
|
||||||
@ -321,9 +321,9 @@ pub async fn list_issues(
|
|||||||
|
|
||||||
sql.push_str(" ORDER BY i.updated_at DESC");
|
sql.push_str(" ORDER BY i.updated_at DESC");
|
||||||
sql.push_str(&format!(
|
sql.push_str(&format!(
|
||||||
" LIMIT ?{} OFFSET ?{}",
|
" LIMIT ?{limit_index} OFFSET ?{offset_index}",
|
||||||
params.len() + 1,
|
limit_index = params.len() + 1,
|
||||||
params.len() + 2
|
offset_index = params.len() + 2
|
||||||
));
|
));
|
||||||
params.push(Box::new(limit));
|
params.push(Box::new(limit));
|
||||||
params.push(Box::new(offset));
|
params.push(Box::new(offset));
|
||||||
|
|||||||
@ -34,7 +34,7 @@ pub async fn generate_rca(
|
|||||||
id: doc_id.clone(),
|
id: doc_id.clone(),
|
||||||
issue_id: issue_id.clone(),
|
issue_id: issue_id.clone(),
|
||||||
doc_type: "rca".to_string(),
|
doc_type: "rca".to_string(),
|
||||||
title: format!("RCA: {}", issue_detail.issue.title),
|
title: format!("RCA: {title}", title = issue_detail.issue.title),
|
||||||
content_md: content_md.clone(),
|
content_md: content_md.clone(),
|
||||||
created_at: now.clone(),
|
created_at: now.clone(),
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
@ -49,7 +49,7 @@ pub async fn generate_rca(
|
|||||||
"doc_title": document.title,
|
"doc_title": document.title,
|
||||||
"content_length": content_md.len(),
|
"content_length": content_md.len(),
|
||||||
"content_preview": if content_md.len() > 300 {
|
"content_preview": if content_md.len() > 300 {
|
||||||
format!("{}...", &content_md[..300])
|
format!("{preview}...", preview = &content_md[..300])
|
||||||
} else {
|
} else {
|
||||||
content_md.clone()
|
content_md.clone()
|
||||||
},
|
},
|
||||||
@ -93,7 +93,7 @@ pub async fn generate_postmortem(
|
|||||||
id: doc_id.clone(),
|
id: doc_id.clone(),
|
||||||
issue_id: issue_id.clone(),
|
issue_id: issue_id.clone(),
|
||||||
doc_type: "postmortem".to_string(),
|
doc_type: "postmortem".to_string(),
|
||||||
title: format!("Post-Mortem: {}", issue_detail.issue.title),
|
title: format!("Post-Mortem: {title}", title = issue_detail.issue.title),
|
||||||
content_md: content_md.clone(),
|
content_md: content_md.clone(),
|
||||||
created_at: now.clone(),
|
created_at: now.clone(),
|
||||||
updated_at: now,
|
updated_at: now,
|
||||||
@ -108,7 +108,7 @@ pub async fn generate_postmortem(
|
|||||||
"doc_title": document.title,
|
"doc_title": document.title,
|
||||||
"content_length": content_md.len(),
|
"content_length": content_md.len(),
|
||||||
"content_preview": if content_md.len() > 300 {
|
"content_preview": if content_md.len() > 300 {
|
||||||
format!("{}...", &content_md[..300])
|
format!("{preview}...", preview = &content_md[..300])
|
||||||
} else {
|
} else {
|
||||||
content_md.clone()
|
content_md.clone()
|
||||||
},
|
},
|
||||||
|
|||||||
@ -514,7 +514,7 @@ pub async fn authenticate_with_webview(
|
|||||||
app_handle: tauri::AppHandle,
|
app_handle: tauri::AppHandle,
|
||||||
app_state: State<'_, AppState>,
|
app_state: State<'_, AppState>,
|
||||||
) -> Result<WebviewAuthResponse, String> {
|
) -> Result<WebviewAuthResponse, String> {
|
||||||
let webview_id = format!("{}-auth", service);
|
let webview_id = format!("{service}-auth");
|
||||||
|
|
||||||
// Check if window already exists
|
// Check if window already exists
|
||||||
if let Some(existing_label) = app_state
|
if let Some(existing_label) = app_state
|
||||||
@ -526,10 +526,7 @@ pub async fn authenticate_with_webview(
|
|||||||
if app_handle.get_webview_window(existing_label).is_some() {
|
if app_handle.get_webview_window(existing_label).is_some() {
|
||||||
return Ok(WebviewAuthResponse {
|
return Ok(WebviewAuthResponse {
|
||||||
success: true,
|
success: true,
|
||||||
message: format!(
|
message: format!("{service} browser window is already open. Switch to it to log in."),
|
||||||
"{} browser window is already open. Switch to it to log in.",
|
|
||||||
service
|
|
||||||
),
|
|
||||||
webview_id: existing_label.clone(),
|
webview_id: existing_label.clone(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -551,8 +548,7 @@ pub async fn authenticate_with_webview(
|
|||||||
Ok(WebviewAuthResponse {
|
Ok(WebviewAuthResponse {
|
||||||
success: true,
|
success: true,
|
||||||
message: format!(
|
message: format!(
|
||||||
"{} browser window opened. This window will stay open - use it to browse and authenticate. Cookies will be extracted automatically for API calls.",
|
"{service} browser window opened. This window will stay open - use it to browse and authenticate. Cookies will be extracted automatically for API calls."
|
||||||
service
|
|
||||||
),
|
),
|
||||||
webview_id,
|
webview_id,
|
||||||
})
|
})
|
||||||
@ -622,7 +618,7 @@ pub async fn extract_cookies_from_webview(
|
|||||||
|
|
||||||
Ok(ConnectionResult {
|
Ok(ConnectionResult {
|
||||||
success: true,
|
success: true,
|
||||||
message: format!("{} authentication saved successfully", service),
|
message: format!("{service} authentication saved successfully"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -669,7 +665,7 @@ pub async fn save_manual_token(
|
|||||||
};
|
};
|
||||||
crate::integrations::servicenow::test_connection(&config).await
|
crate::integrations::servicenow::test_connection(&config).await
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Unknown service: {}", request.service)),
|
_ => return Err(format!("Unknown service: {service}", service = request.service)),
|
||||||
};
|
};
|
||||||
|
|
||||||
// If test fails, don't save the token
|
// If test fails, don't save the token
|
||||||
@ -736,7 +732,10 @@ pub async fn save_manual_token(
|
|||||||
|
|
||||||
Ok(ConnectionResult {
|
Ok(ConnectionResult {
|
||||||
success: true,
|
success: true,
|
||||||
message: format!("{} token saved and validated successfully", request.service),
|
message: format!(
|
||||||
|
"{service} token saved and validated successfully",
|
||||||
|
service = request.service
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -98,20 +98,23 @@ pub async fn get_audit_log(
|
|||||||
let mut params: Vec<Box<dyn rusqlite::types::ToSql>> = vec![];
|
let mut params: Vec<Box<dyn rusqlite::types::ToSql>> = vec![];
|
||||||
|
|
||||||
if let Some(ref action) = filter.action {
|
if let Some(ref action) = filter.action {
|
||||||
sql.push_str(&format!(" AND action = ?{}", params.len() + 1));
|
sql.push_str(&format!(" AND action = ?{index}", index = params.len() + 1));
|
||||||
params.push(Box::new(action.clone()));
|
params.push(Box::new(action.clone()));
|
||||||
}
|
}
|
||||||
if let Some(ref entity_type) = filter.entity_type {
|
if let Some(ref entity_type) = filter.entity_type {
|
||||||
sql.push_str(&format!(" AND entity_type = ?{}", params.len() + 1));
|
sql.push_str(&format!(
|
||||||
|
" AND entity_type = ?{index}",
|
||||||
|
index = params.len() + 1
|
||||||
|
));
|
||||||
params.push(Box::new(entity_type.clone()));
|
params.push(Box::new(entity_type.clone()));
|
||||||
}
|
}
|
||||||
if let Some(ref entity_id) = filter.entity_id {
|
if let Some(ref entity_id) = filter.entity_id {
|
||||||
sql.push_str(&format!(" AND entity_id = ?{}", params.len() + 1));
|
sql.push_str(&format!(" AND entity_id = ?{index}", index = params.len() + 1));
|
||||||
params.push(Box::new(entity_id.clone()));
|
params.push(Box::new(entity_id.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
sql.push_str(" ORDER BY timestamp DESC");
|
sql.push_str(" ORDER BY timestamp DESC");
|
||||||
sql.push_str(&format!(" LIMIT ?{}", params.len() + 1));
|
sql.push_str(&format!(" LIMIT ?{index}", index = params.len() + 1));
|
||||||
params.push(Box::new(limit));
|
params.push(Box::new(limit));
|
||||||
|
|
||||||
let param_refs: Vec<&dyn rusqlite::types::ToSql> = params.iter().map(|p| p.as_ref()).collect();
|
let param_refs: Vec<&dyn rusqlite::types::ToSql> = params.iter().map(|p| p.as_ref()).collect();
|
||||||
|
|||||||
@ -5,15 +5,21 @@ pub fn generate_postmortem_markdown(detail: &IssueDetail) -> String {
|
|||||||
|
|
||||||
let mut md = String::new();
|
let mut md = String::new();
|
||||||
|
|
||||||
md.push_str(&format!("# Blameless Post-Mortem: {}\n\n", issue.title));
|
md.push_str(&format!(
|
||||||
|
"# Blameless Post-Mortem: {title}\n\n",
|
||||||
|
title = issue.title
|
||||||
|
));
|
||||||
|
|
||||||
// Header metadata
|
// Header metadata
|
||||||
md.push_str("## Metadata\n\n");
|
md.push_str("## Metadata\n\n");
|
||||||
md.push_str(&format!("- **Date:** {}\n", issue.created_at));
|
md.push_str(&format!("- **Date:** {created_at}\n", created_at = issue.created_at));
|
||||||
md.push_str(&format!("- **Severity:** {}\n", issue.severity));
|
md.push_str(&format!("- **Severity:** {severity}\n", severity = issue.severity));
|
||||||
md.push_str(&format!("- **Category:** {}\n", issue.category));
|
md.push_str(&format!("- **Category:** {category}\n", category = issue.category));
|
||||||
md.push_str(&format!("- **Status:** {}\n", issue.status));
|
md.push_str(&format!("- **Status:** {status}\n", status = issue.status));
|
||||||
md.push_str(&format!("- **Last Updated:** {}\n", issue.updated_at));
|
md.push_str(&format!(
|
||||||
|
"- **Last Updated:** {updated_at}\n",
|
||||||
|
updated_at = issue.updated_at
|
||||||
|
));
|
||||||
md.push_str(&format!(
|
md.push_str(&format!(
|
||||||
"- **Assigned To:** {}\n",
|
"- **Assigned To:** {}\n",
|
||||||
if issue.assigned_to.is_empty() {
|
if issue.assigned_to.is_empty() {
|
||||||
@ -45,7 +51,7 @@ pub fn generate_postmortem_markdown(detail: &IssueDetail) -> String {
|
|||||||
md.push_str("## Timeline\n\n");
|
md.push_str("## Timeline\n\n");
|
||||||
md.push_str("| Time (UTC) | Event |\n");
|
md.push_str("| Time (UTC) | Event |\n");
|
||||||
md.push_str("|------------|-------|\n");
|
md.push_str("|------------|-------|\n");
|
||||||
md.push_str(&format!("| {} | Issue created |\n", issue.created_at));
|
md.push_str(&format!("| {created_at} | Issue created |\n", created_at = issue.created_at));
|
||||||
if let Some(ref resolved) = issue.resolved_at {
|
if let Some(ref resolved) = issue.resolved_at {
|
||||||
md.push_str(&format!("| {resolved} | Issue resolved |\n"));
|
md.push_str(&format!("| {resolved} | Issue resolved |\n"));
|
||||||
}
|
}
|
||||||
@ -77,7 +83,7 @@ pub fn generate_postmortem_markdown(detail: &IssueDetail) -> String {
|
|||||||
|
|
||||||
if let Some(last) = detail.resolution_steps.last() {
|
if let Some(last) = detail.resolution_steps.last() {
|
||||||
if !last.answer.is_empty() {
|
if !last.answer.is_empty() {
|
||||||
md.push_str(&format!("**Root Cause:** {}\n\n", last.answer));
|
md.push_str(&format!("**Root Cause:** {answer}\n\n", answer = last.answer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,16 +5,16 @@ pub fn generate_rca_markdown(detail: &IssueDetail) -> String {
|
|||||||
|
|
||||||
let mut md = String::new();
|
let mut md = String::new();
|
||||||
|
|
||||||
md.push_str(&format!("# Root Cause Analysis: {}\n\n", issue.title));
|
md.push_str(&format!("# Root Cause Analysis: {title}\n\n", title = issue.title));
|
||||||
|
|
||||||
md.push_str("## Issue Summary\n\n");
|
md.push_str("## Issue Summary\n\n");
|
||||||
md.push_str("| Field | Value |\n");
|
md.push_str("| Field | Value |\n");
|
||||||
md.push_str("|-------|-------|\n");
|
md.push_str("|-------|-------|\n");
|
||||||
md.push_str(&format!("| **Issue ID** | {} |\n", issue.id));
|
md.push_str(&format!("| **Issue ID** | {id} |\n", id = issue.id));
|
||||||
md.push_str(&format!("| **Category** | {} |\n", issue.category));
|
md.push_str(&format!("| **Category** | {category} |\n", category = issue.category));
|
||||||
md.push_str(&format!("| **Status** | {} |\n", issue.status));
|
md.push_str(&format!("| **Status** | {status} |\n", status = issue.status));
|
||||||
md.push_str(&format!("| **Severity** | {} |\n", issue.severity));
|
md.push_str(&format!("| **Severity** | {severity} |\n", severity = issue.severity));
|
||||||
md.push_str(&format!("| **Source** | {} |\n", issue.source));
|
md.push_str(&format!("| **Source** | {source} |\n", source = issue.source));
|
||||||
md.push_str(&format!(
|
md.push_str(&format!(
|
||||||
"| **Assigned To** | {} |\n",
|
"| **Assigned To** | {} |\n",
|
||||||
if issue.assigned_to.is_empty() {
|
if issue.assigned_to.is_empty() {
|
||||||
@ -23,8 +23,11 @@ pub fn generate_rca_markdown(detail: &IssueDetail) -> String {
|
|||||||
&issue.assigned_to
|
&issue.assigned_to
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
md.push_str(&format!("| **Created** | {} |\n", issue.created_at));
|
md.push_str(&format!("| **Created** | {created_at} |\n", created_at = issue.created_at));
|
||||||
md.push_str(&format!("| **Last Updated** | {} |\n", issue.updated_at));
|
md.push_str(&format!(
|
||||||
|
"| **Last Updated** | {updated_at} |\n",
|
||||||
|
updated_at = issue.updated_at
|
||||||
|
));
|
||||||
if let Some(ref resolved) = issue.resolved_at {
|
if let Some(ref resolved) = issue.resolved_at {
|
||||||
md.push_str(&format!("| **Resolved** | {resolved} |\n"));
|
md.push_str(&format!("| **Resolved** | {resolved} |\n"));
|
||||||
}
|
}
|
||||||
@ -47,12 +50,12 @@ pub fn generate_rca_markdown(detail: &IssueDetail) -> String {
|
|||||||
step.step_order, step.why_question
|
step.step_order, step.why_question
|
||||||
));
|
));
|
||||||
if !step.answer.is_empty() {
|
if !step.answer.is_empty() {
|
||||||
md.push_str(&format!("**Answer:** {}\n\n", step.answer));
|
md.push_str(&format!("**Answer:** {answer}\n\n", answer = step.answer));
|
||||||
} else {
|
} else {
|
||||||
md.push_str("_Awaiting answer._\n\n");
|
md.push_str("_Awaiting answer._\n\n");
|
||||||
}
|
}
|
||||||
if !step.evidence.is_empty() {
|
if !step.evidence.is_empty() {
|
||||||
md.push_str(&format!("**Evidence:** {}\n\n", step.evidence));
|
md.push_str(&format!("**Evidence:** {evidence}\n\n", evidence = step.evidence));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -365,7 +365,7 @@ mod tests {
|
|||||||
.create_async()
|
.create_async()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let token_endpoint = format!("{}/oauth/token", server.url());
|
let token_endpoint = format!("{server_url}/oauth/token", server_url = server.url());
|
||||||
let result = exchange_code(
|
let result = exchange_code(
|
||||||
&token_endpoint,
|
&token_endpoint,
|
||||||
"test-client-id",
|
"test-client-id",
|
||||||
@ -397,7 +397,7 @@ mod tests {
|
|||||||
.create_async()
|
.create_async()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let token_endpoint = format!("{}/oauth/token", server.url());
|
let token_endpoint = format!("{server_url}/oauth/token", server_url = server.url());
|
||||||
let result = exchange_code(
|
let result = exchange_code(
|
||||||
&token_endpoint,
|
&token_endpoint,
|
||||||
"test-client-id",
|
"test-client-id",
|
||||||
@ -421,7 +421,7 @@ mod tests {
|
|||||||
.create_async()
|
.create_async()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let token_endpoint = format!("{}/oauth/token", server.url());
|
let token_endpoint = format!("{server_url}/oauth/token", server_url = server.url());
|
||||||
let result = exchange_code(
|
let result = exchange_code(
|
||||||
&token_endpoint,
|
&token_endpoint,
|
||||||
"test-client-id",
|
"test-client-id",
|
||||||
|
|||||||
@ -40,9 +40,10 @@ pub async fn test_connection(config: &AzureDevOpsConfig) -> Result<ConnectionRes
|
|||||||
message: "Successfully connected to Azure DevOps".to_string(),
|
message: "Successfully connected to Azure DevOps".to_string(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
let status = resp.status();
|
||||||
Ok(ConnectionResult {
|
Ok(ConnectionResult {
|
||||||
success: false,
|
success: false,
|
||||||
message: format!("Connection failed with status: {}", resp.status()),
|
message: format!("Connection failed with status: {status}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,8 +62,7 @@ pub async fn search_work_items(
|
|||||||
|
|
||||||
// Build WIQL query
|
// Build WIQL query
|
||||||
let wiql = format!(
|
let wiql = format!(
|
||||||
"SELECT [System.Id], [System.Title], [System.WorkItemType], [System.State] FROM WorkItems WHERE [System.Title] CONTAINS '{}' ORDER BY [System.CreatedDate] DESC",
|
"SELECT [System.Id], [System.Title], [System.WorkItemType], [System.State] FROM WorkItems WHERE [System.Title] CONTAINS '{query}' ORDER BY [System.CreatedDate] DESC"
|
||||||
query
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let body = serde_json::json!({ "query": wiql });
|
let body = serde_json::json!({ "query": wiql });
|
||||||
|
|||||||
@ -269,7 +269,7 @@ mod tests {
|
|||||||
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
|
||||||
|
|
||||||
// Server should be running
|
// Server should be running
|
||||||
let health_url = format!("http://127.0.0.1:{}/health", port);
|
let health_url = format!("http://127.0.0.1:{port}/health");
|
||||||
let health_before = reqwest::get(&health_url).await;
|
let health_before = reqwest::get(&health_url).await;
|
||||||
assert!(health_before.is_ok(), "Server should be running");
|
assert!(health_before.is_ok(), "Server should be running");
|
||||||
|
|
||||||
|
|||||||
@ -43,9 +43,10 @@ pub async fn test_connection(config: &ConfluenceConfig) -> Result<ConnectionResu
|
|||||||
message: "Successfully connected to Confluence".to_string(),
|
message: "Successfully connected to Confluence".to_string(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
let status = resp.status();
|
||||||
Ok(ConnectionResult {
|
Ok(ConnectionResult {
|
||||||
success: false,
|
success: false,
|
||||||
message: format!("Connection failed with status: {}", resp.status()),
|
message: format!("Connection failed with status: {status}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +54,8 @@ pub async fn test_connection(config: &ConfluenceConfig) -> Result<ConnectionResu
|
|||||||
/// List all spaces accessible with the current token
|
/// List all spaces accessible with the current token
|
||||||
pub async fn list_spaces(config: &ConfluenceConfig) -> Result<Vec<Space>, String> {
|
pub async fn list_spaces(config: &ConfluenceConfig) -> Result<Vec<Space>, String> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let url = format!("{}/rest/api/space", config.base_url.trim_end_matches('/'));
|
let base_url = config.base_url.trim_end_matches('/');
|
||||||
|
let url = format!("{base_url}/rest/api/space");
|
||||||
|
|
||||||
let resp = client
|
let resp = client
|
||||||
.get(&url)
|
.get(&url)
|
||||||
@ -103,9 +105,9 @@ pub async fn search_pages(
|
|||||||
config.base_url.trim_end_matches('/')
|
config.base_url.trim_end_matches('/')
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut cql = format!("text ~ \"{}\"", query);
|
let mut cql = format!("text ~ \"{query}\"");
|
||||||
if let Some(space) = space_key {
|
if let Some(space) = space_key {
|
||||||
cql = format!("{} AND space = {}", cql, space);
|
cql = format!("{cql} AND space = {space}");
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp = client
|
let resp = client
|
||||||
@ -140,7 +142,7 @@ pub async fn search_pages(
|
|||||||
id: page_id.to_string(),
|
id: page_id.to_string(),
|
||||||
title: p["title"].as_str()?.to_string(),
|
title: p["title"].as_str()?.to_string(),
|
||||||
space_key: p["space"]["key"].as_str()?.to_string(),
|
space_key: p["space"]["key"].as_str()?.to_string(),
|
||||||
url: format!("{}/pages/viewpage.action?pageId={}", base_url, page_id),
|
url: format!("{base_url}/pages/viewpage.action?pageId={page_id}"),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -157,7 +159,8 @@ pub async fn publish_page(
|
|||||||
parent_page_id: Option<&str>,
|
parent_page_id: Option<&str>,
|
||||||
) -> Result<PublishResult, String> {
|
) -> Result<PublishResult, String> {
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let url = format!("{}/rest/api/content", config.base_url.trim_end_matches('/'));
|
let base_url = config.base_url.trim_end_matches('/');
|
||||||
|
let url = format!("{base_url}/rest/api/content");
|
||||||
|
|
||||||
let mut body = serde_json::json!({
|
let mut body = serde_json::json!({
|
||||||
"type": "page",
|
"type": "page",
|
||||||
|
|||||||
@ -42,9 +42,10 @@ pub async fn test_connection(config: &ServiceNowConfig) -> Result<ConnectionResu
|
|||||||
message: "Successfully connected to ServiceNow".to_string(),
|
message: "Successfully connected to ServiceNow".to_string(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
let status = resp.status();
|
||||||
Ok(ConnectionResult {
|
Ok(ConnectionResult {
|
||||||
success: false,
|
success: false,
|
||||||
message: format!("Connection failed with status: {}", resp.status()),
|
message: format!("Connection failed with status: {status}"),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,13 +25,14 @@ pub async fn authenticate_with_webview(
|
|||||||
service: &str,
|
service: &str,
|
||||||
base_url: &str,
|
base_url: &str,
|
||||||
) -> Result<ExtractedCredentials, String> {
|
) -> Result<ExtractedCredentials, String> {
|
||||||
|
let trimmed_base_url = base_url.trim_end_matches('/');
|
||||||
let login_url = match service {
|
let login_url = match service {
|
||||||
"confluence" => format!("{}/login.action", base_url.trim_end_matches('/')),
|
"confluence" => format!("{trimmed_base_url}/login.action"),
|
||||||
"azuredevops" => {
|
"azuredevops" => {
|
||||||
// Azure DevOps login - user will be redirected through Microsoft SSO
|
// Azure DevOps login - user will be redirected through Microsoft SSO
|
||||||
format!("{}/_signin", base_url.trim_end_matches('/'))
|
format!("{trimmed_base_url}/_signin")
|
||||||
}
|
}
|
||||||
"servicenow" => format!("{}/login.do", base_url.trim_end_matches('/')),
|
"servicenow" => format!("{trimmed_base_url}/login.do"),
|
||||||
_ => return Err(format!("Unknown service: {service}")),
|
_ => return Err(format!("Unknown service: {service}")),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,13 +43,13 @@ pub async fn authenticate_with_webview(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create persistent browser window (stays open for browsing and fresh cookie extraction)
|
// Create persistent browser window (stays open for browsing and fresh cookie extraction)
|
||||||
let webview_label = format!("{}-auth", service);
|
let webview_label = format!("{service}-auth");
|
||||||
let webview = WebviewWindowBuilder::new(
|
let webview = WebviewWindowBuilder::new(
|
||||||
&app_handle,
|
&app_handle,
|
||||||
&webview_label,
|
&webview_label,
|
||||||
WebviewUrl::External(login_url.parse().map_err(|e| format!("Invalid URL: {e}"))?),
|
WebviewUrl::External(login_url.parse().map_err(|e| format!("Invalid URL: {e}"))?),
|
||||||
)
|
)
|
||||||
.title(format!("{} Browser (TFTSR)", service))
|
.title(format!("{service} Browser (TFTSR)"))
|
||||||
.inner_size(1000.0, 800.0)
|
.inner_size(1000.0, 800.0)
|
||||||
.min_inner_size(800.0, 600.0)
|
.min_inner_size(800.0, 600.0)
|
||||||
.resizable(true)
|
.resizable(true)
|
||||||
@ -195,7 +196,7 @@ pub async fn extract_cookies_via_ipc<R: tauri::Runtime>(
|
|||||||
pub fn cookies_to_header(cookies: &[Cookie]) -> String {
|
pub fn cookies_to_header(cookies: &[Cookie]) -> String {
|
||||||
cookies
|
cookies
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| format!("{}={}", c.name, c.value))
|
.map(|c| format!("{name}={value}", name = c.name.as_str(), value = c.value.as_str()))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("; ")
|
.join("; ")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user