@@ -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