Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically download attachments in Azure DevOps?

I'm trying to programmatically download 90+ attachments from a user story in VSTS using TeamFoundation/VisualStudio C# API that I downloaded from Nuget. I was trying to use this example: https://intellitect.com/downloading-attachments-from-tfs/

However, that code seems to be out of date. I cannot seem to find these exact packages mentioned in article: nuget-bot.Microsoft.TeamFoundation.Client nuget-bot.Microsoft.TeamFoundation.WorkItemTracking.Client

However, I downloaded TFS packages that are there such as Microsoft.TeamFoundationServer.Client and Microsoft.TeamFoundationServer.ExtendedClient but the WorkItem class does not seem to have an Attachments no more. Does somebody know where I can find the Attachments property? I browsed the Object Browser in Visual Studio and cannot find it. Or can you suggest an alternate solution to get attachments from a work item? Thanks.

like image 674
ray500 Avatar asked May 01 '18 15:05

ray500


2 Answers

WebClient cannot authenticate to VSTS correctly. Instead of using WebClient to download the file, you can use WorkItemServer.DownloadFile()method in Microsoft.TeamFoundation.WorkItemTracking.Proxy to download the file. See this thread for details.

You can use below sample to download the attachment for a specific work item:

Note: Install the the nuget package Microsoft.TeamFoundationServer.ExtendedClient

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Proxy;
using System;
using System.IO;

namespace VSTS_DownloadWITAttachment
{
    class Program
    {
        static void Main(string[] args)
        {
            TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(new Uri("https://account.visualstudio.com/"));
            ttpc.EnsureAuthenticated();
            WorkItemStore wistore = ttpc.GetService<WorkItemStore>();
            WorkItem wi = wistore.GetWorkItem(94);
            WorkItemServer wiserver = ttpc.GetService<WorkItemServer>();
            string tmppath = wiserver.DownloadFile(wi.Attachments[0].Id); //Change the number to download other attachments if there are more then one attachments for the specific work item. e.g: [1] to download the second one.
            string filename = string.Format("D:\\WITAttachments\\{0}-{1}", wi.Fields["ID"].Value, wi.Attachments[0].Name);
            File.Copy(tmppath, filename);
        }
    }
}

Then you can try to query the work items and download the attachments in a loop for each of them. See Fetch work items with queries programatically in VSTS for details.

Below sample for your reference:

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Proxy;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;

namespace DownloadWITAttachments
{
    class Program
    {
        static void Main(string[] args)
        {
            Uri uri = new Uri("https://account.visualstudio.com");
            string PAT = "xxxxxxxxxxxx";
            string project = "ProjectName";

            VssBasicCredential credentials = new VssBasicCredential("", PAT);

            //create a wiql object and build our query
            Wiql wiql = new Wiql()
            {
                Query = "Select * " +
                        "From WorkItems " +
                        "Where [Work Item Type] = 'User Story' " +
                        "And [System.TeamProject] = '" + project + "' " +
                        "And [System.State] <> 'Closed' " +
                        "And [System.AttachedFileCount] > 0 " +
                        "Order By [State] Asc, [Changed Date] Desc"
            };

            //create instance of work item tracking http client
            using (WorkItemTrackingHttpClient workItemTrackingHttpClient = new WorkItemTrackingHttpClient(uri, credentials))
            {
                //execute the query to get the list of work items in the results
                WorkItemQueryResult workItemQueryResult = workItemTrackingHttpClient.QueryByWiqlAsync(wiql).Result;

                if (workItemQueryResult.WorkItems.Count() != 0)
                {
                    //Download the first attachment for each work item.
                    foreach (var item in workItemQueryResult.WorkItems)
                    {
                        TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(uri);
                        ttpc.EnsureAuthenticated();
                        WorkItemStore wistore = ttpc.GetService<WorkItemStore>();
                        Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem wi = wistore.GetWorkItem(item.Id);
                        WorkItemServer wiserver = ttpc.GetService<WorkItemServer>();                       
                        string tmppath = wiserver.DownloadFile(wi.Attachments[0].Id);
                        string filename = string.Format("D:\\temp\\vsts\\{0}-{1}", wi.Fields["ID"].Value, wi.Attachments[0].Name);
                        File.Copy(tmppath, filename);
                    }
                }
            }
        }
    }
}
like image 155
Andy Li-MSFT Avatar answered Sep 27 '22 23:09

Andy Li-MSFT


If you don't already have an access token for interacting with the API, generate one.

Make an API call to get the expanded work item information. Note the $expand=all parameter to retrieve all item details.

GET https://{account}.visualstudio.com/{project}/_apis/wit/workitems/115258?api-version=4.1&$expand=all

The response should look something like this (if the item has attachments).

{
    "id": 115258,
    "rev": 4,
    "fields": {
        "System.Id": 115258,
        "System.AreaId": 2643
        ...and so on...
    },
    "relations": [
        {
            "rel": "AttachedFile",
            "url": "https://{account}.visualstudio.com/d6c4b828-0f7e-4b69-a356-a92c0ec3cd07/_apis/wit/attachments/5682f031-4b09-478c-8042-0d2a998905e4",
            "attributes": {
                "authorizedDate": "2018-04-30T19:34:09.763Z",
                "id": 2015371,
                "resourceCreatedDate": "2018-04-30T19:34:07.873Z",
                "resourceModifiedDate": "2018-04-30T19:32:16.057Z",
                "revisedDate": "9999-01-01T00:00:00Z",
                "resourceSize": 47104,
                "name": "file.jpg"
            }
        }
    ]
}

Iterate over the relations where the rel is AttachedFile and call the url to get the attachment content.

GET https://{account}.visualstudio.com/d6c4b828-0f7e-4b69-a356-a92c0ec3cd07/_apis/wit/attachments/5682f031-4b09-478c-8042-0d2a998905e4

Sources:

  • Work Items - Get Work Item
  • Attachments - Get
like image 39
Dan Wilson Avatar answered Sep 27 '22 23:09

Dan Wilson