Skip to content

Commit 96b109f

Browse files
refactor(safeoutputs): reduce complexity of file_or_append_work_item (#1073)
Extract the two HTTP-operation branches of file_or_append_work_item (cognitive complexity 19/15) into focused helpers: - append_comment_to_work_item — POSTs a comment to an existing work item - create_new_work_item — POSTs a new work item via the patch-doc API The top-level function now contains only guard checks and a two-line dispatch; each helper owns exactly one HTTP round-trip with its own success/failure result handling. Before: cognitive complexity 19 (threshold 15) After: cognitive complexity < 15 (zero warnings at threshold 15) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 8d8dbb2 commit 96b109f

1 file changed

Lines changed: 141 additions & 124 deletions

File tree

src/safeoutputs/mod.rs

Lines changed: 141 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -542,142 +542,159 @@ pub(crate) async fn file_or_append_work_item(
542542
let body_with_stats = crate::agent_stats::append_stats_to_body(body, ctx, config.include_stats);
543543

544544
if let Some(work_item_id) = existing_id {
545-
// Append a comment to the existing work item
546545
debug!(
547546
"Found existing work item #{}, appending comment",
548547
work_item_id
549548
);
550-
let comment_payload = serde_json::json!({ "text": body_with_stats });
551-
552-
let url = format!(
553-
"{}/{}/_apis/wit/workItems/{}/comments?api-version=7.1-preview.4",
554-
org_url.trim_end_matches('/'),
555-
utf8_percent_encode(project, PATH_SEGMENT),
556-
work_item_id,
557-
);
558-
559-
let resp = client
560-
.post(&url)
561-
.header("Content-Type", "application/json")
562-
.basic_auth("", Some(token))
563-
.json(&comment_payload)
564-
.send()
565-
.await
566-
.context("Failed to add comment to work item")?;
567-
568-
if resp.status().is_success() {
569-
let resp_body: serde_json::Value = resp
570-
.json()
571-
.await
572-
.context("Failed to parse comment response")?;
573-
let comment_id = resp_body.get("id").and_then(|v| v.as_i64());
574-
let message = match comment_id {
575-
Some(id) => format!(
576-
"Appended comment #{} to existing work item #{}: {}",
577-
id, work_item_id, title
578-
),
579-
None => format!(
580-
"Appended comment to existing work item #{}: {}",
581-
work_item_id, title
582-
),
583-
};
584-
Ok(ExecutionResult::success_with_data(
585-
message,
586-
serde_json::json!({
587-
"action": "appended",
588-
"work_item_id": work_item_id,
589-
"comment_id": comment_id,
590-
}),
591-
))
592-
} else {
593-
let status = resp.status();
594-
let error_body = resp
595-
.text()
596-
.await
597-
.unwrap_or_else(|_| "Unknown error".to_string());
598-
Ok(ExecutionResult::failure(format!(
599-
"Failed to append comment to work item #{} (HTTP {}): {}",
600-
work_item_id, status, error_body
601-
)))
602-
}
549+
append_comment_to_work_item(&client, org_url, project, token, title, work_item_id, &body_with_stats).await
603550
} else {
604-
// Create a new work item
605551
debug!("No existing work item found, creating new one");
552+
create_new_work_item(&client, org_url, project, token, title, &body_with_stats, config).await
553+
}
554+
}
606555

607-
let mut patch_doc = vec![
608-
serde_json::json!({"op": "add", "path": "/fields/System.Title", "value": title}),
609-
serde_json::json!({"op": "add", "path": "/fields/System.Description", "value": body_with_stats}),
610-
serde_json::json!({"op": "add", "path": "/multilineFieldsFormat/System.Description", "value": "Markdown"}),
611-
];
556+
/// POST a comment to an existing Azure DevOps work item.
557+
async fn append_comment_to_work_item(
558+
client: &reqwest::Client,
559+
org_url: &str,
560+
project: &str,
561+
token: &str,
562+
title: &str,
563+
work_item_id: i64,
564+
body: &str,
565+
) -> anyhow::Result<ExecutionResult> {
566+
let url = format!(
567+
"{}/{}/_apis/wit/workItems/{}/comments?api-version=7.1-preview.4",
568+
org_url.trim_end_matches('/'),
569+
utf8_percent_encode(project, PATH_SEGMENT),
570+
work_item_id,
571+
);
572+
let resp = client
573+
.post(&url)
574+
.header("Content-Type", "application/json")
575+
.basic_auth("", Some(token))
576+
.json(&serde_json::json!({ "text": body }))
577+
.send()
578+
.await
579+
.context("Failed to add comment to work item")?;
612580

613-
if let Some(area_path) = &config.area_path {
614-
patch_doc.push(
615-
serde_json::json!({"op": "add", "path": "/fields/System.AreaPath", "value": area_path}),
616-
);
617-
}
618-
if let Some(iteration_path) = &config.iteration_path {
619-
patch_doc.push(
620-
serde_json::json!({"op": "add", "path": "/fields/System.IterationPath", "value": iteration_path}),
621-
);
622-
}
623-
if !config.tags.is_empty() {
624-
patch_doc.push(
625-
serde_json::json!({"op": "add", "path": "/fields/System.Tags", "value": config.tags.join("; ")}),
626-
);
627-
}
581+
if resp.status().is_success() {
582+
let resp_body: serde_json::Value = resp
583+
.json()
584+
.await
585+
.context("Failed to parse comment response")?;
586+
let comment_id = resp_body.get("id").and_then(|v| v.as_i64());
587+
let message = match comment_id {
588+
Some(id) => format!(
589+
"Appended comment #{} to existing work item #{}: {}",
590+
id, work_item_id, title
591+
),
592+
None => format!(
593+
"Appended comment to existing work item #{}: {}",
594+
work_item_id, title
595+
),
596+
};
597+
Ok(ExecutionResult::success_with_data(
598+
message,
599+
serde_json::json!({
600+
"action": "appended",
601+
"work_item_id": work_item_id,
602+
"comment_id": comment_id,
603+
}),
604+
))
605+
} else {
606+
let status = resp.status();
607+
let error_body = resp
608+
.text()
609+
.await
610+
.unwrap_or_else(|_| "Unknown error".to_string());
611+
Ok(ExecutionResult::failure(format!(
612+
"Failed to append comment to work item #{} (HTTP {}): {}",
613+
work_item_id, status, error_body
614+
)))
615+
}
616+
}
628617

629-
let url = format!(
630-
"{}/{}/_apis/wit/workitems/${}?api-version=7.0",
631-
org_url.trim_end_matches('/'),
632-
utf8_percent_encode(project, PATH_SEGMENT),
633-
utf8_percent_encode(&config.work_item_type, PATH_SEGMENT),
618+
/// POST a new Azure DevOps work item using the patch-document API.
619+
async fn create_new_work_item(
620+
client: &reqwest::Client,
621+
org_url: &str,
622+
project: &str,
623+
token: &str,
624+
title: &str,
625+
body: &str,
626+
config: &WorkItemReportConfig,
627+
) -> anyhow::Result<ExecutionResult> {
628+
let mut patch_doc = vec![
629+
serde_json::json!({"op": "add", "path": "/fields/System.Title", "value": title}),
630+
serde_json::json!({"op": "add", "path": "/fields/System.Description", "value": body}),
631+
serde_json::json!({"op": "add", "path": "/multilineFieldsFormat/System.Description", "value": "Markdown"}),
632+
];
633+
if let Some(area_path) = &config.area_path {
634+
patch_doc.push(
635+
serde_json::json!({"op": "add", "path": "/fields/System.AreaPath", "value": area_path}),
636+
);
637+
}
638+
if let Some(iteration_path) = &config.iteration_path {
639+
patch_doc.push(
640+
serde_json::json!({"op": "add", "path": "/fields/System.IterationPath", "value": iteration_path}),
641+
);
642+
}
643+
if !config.tags.is_empty() {
644+
patch_doc.push(
645+
serde_json::json!({"op": "add", "path": "/fields/System.Tags", "value": config.tags.join("; ")}),
634646
);
647+
}
648+
let url = format!(
649+
"{}/{}/_apis/wit/workitems/${}?api-version=7.0",
650+
org_url.trim_end_matches('/'),
651+
utf8_percent_encode(project, PATH_SEGMENT),
652+
utf8_percent_encode(&config.work_item_type, PATH_SEGMENT),
653+
);
654+
let resp = client
655+
.post(&url)
656+
.header("Content-Type", "application/json-patch+json")
657+
.basic_auth("", Some(token))
658+
.json(&patch_doc)
659+
.send()
660+
.await
661+
.context("Failed to create work item")?;
635662

636-
let resp = client
637-
.post(&url)
638-
.header("Content-Type", "application/json-patch+json")
639-
.basic_auth("", Some(token))
640-
.json(&patch_doc)
641-
.send()
663+
if resp.status().is_success() {
664+
let resp_body: serde_json::Value = resp
665+
.json()
642666
.await
643-
.context("Failed to create work item")?;
644-
645-
if resp.status().is_success() {
646-
let resp_body: serde_json::Value = resp
647-
.json()
648-
.await
649-
.context("Failed to parse work item response")?;
650-
let work_item_id = resp_body.get("id").and_then(|v| v.as_i64());
651-
let work_item_url = resp_body
652-
.get("_links")
653-
.and_then(|l| l.get("html"))
654-
.and_then(|h| h.get("href"))
655-
.and_then(|h| h.as_str())
656-
.unwrap_or("")
657-
.to_string();
658-
let message = match work_item_id {
659-
Some(id) => format!("Created work item #{}: {}", id, title),
660-
None => format!("Created work item: {}", title),
661-
};
662-
Ok(ExecutionResult::success_with_data(
663-
message,
664-
serde_json::json!({
665-
"action": "created",
666-
"work_item_id": work_item_id,
667-
"url": work_item_url,
668-
}),
669-
))
670-
} else {
671-
let status = resp.status();
672-
let error_body = resp
673-
.text()
674-
.await
675-
.unwrap_or_else(|_| "Unknown error".to_string());
676-
Ok(ExecutionResult::failure(format!(
677-
"Failed to create work item (HTTP {}): {}",
678-
status, error_body
679-
)))
680-
}
667+
.context("Failed to parse work item response")?;
668+
let work_item_id = resp_body.get("id").and_then(|v| v.as_i64());
669+
let work_item_url = resp_body
670+
.get("_links")
671+
.and_then(|l| l.get("html"))
672+
.and_then(|h| h.get("href"))
673+
.and_then(|h| h.as_str())
674+
.unwrap_or("")
675+
.to_string();
676+
let message = match work_item_id {
677+
Some(id) => format!("Created work item #{}: {}", id, title),
678+
None => format!("Created work item: {}", title),
679+
};
680+
Ok(ExecutionResult::success_with_data(
681+
message,
682+
serde_json::json!({
683+
"action": "created",
684+
"work_item_id": work_item_id,
685+
"url": work_item_url,
686+
}),
687+
))
688+
} else {
689+
let status = resp.status();
690+
let error_body = resp
691+
.text()
692+
.await
693+
.unwrap_or_else(|_| "Unknown error".to_string());
694+
Ok(ExecutionResult::failure(format!(
695+
"Failed to create work item (HTTP {}): {}",
696+
status, error_body
697+
)))
681698
}
682699
}
683700

0 commit comments

Comments
 (0)