DEV Community

Cover image for Use .Net Interactive with Azure DevOps
Antoine
Antoine

Posted on

2 1

Use .Net Interactive with Azure DevOps

Photo by Dev Benjamin on Unsplash

I will use .Net interactive to connect to Azure DevOps, and generate markdown with the output of one query.

Connect to Azure DevOps

First, we will have to set a PAT for authentication.

string pat = "<YOUR PAT>";
Enter fullscreen mode Exit fullscreen mode

Then, we have to define locally the type representing WorkItem.

class WorkItemPresentation
{
    public string Id {get; set;}
    public string Name {get; set;}
    public string State {get; set;}
    public string Description {get; set;}
}
Enter fullscreen mode Exit fullscreen mode

Finally, we will call the query, map the output to WorkItemPresentation, and output it.

#r "nuget:Microsoft.TeamFoundationServer.Client"
#r "nuget:Microsoft.VisualStudio.Services.InteractiveClient,16.170.0"
#r "nuget:Microsoft.VisualStudio.Services.Client"

// https://www.nuget.org/packages/Microsoft.TeamFoundationServer.Client/
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
// https://www.nuget.org/packages/Microsoft.VisualStudio.Services.InteractiveClient/
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.WebApi;
// https://www.nuget.org/packages/Microsoft.VisualStudio.Services.Client/
using Microsoft.VisualStudio.Services.Common; 

    VssConnection connection = new VssConnection(new Uri("https://XXX.visualstudio.com"), new VssBasicCredential(string.Empty, pat));

    // Create instance of WorkItemTrackingHttpClient using VssConnection
    WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();

    // Get 2 levels of query hierarchy items
    List<QueryHierarchyItem> queryHierarchyItems = witClient.GetQueriesAsync("<PROJECT>", depth: 2).Result;

    // Search for 'My Queries' folder
    QueryHierarchyItem myQueriesFolder = queryHierarchyItems.FirstOrDefault(qhi => qhi.Name.Equals("My Queries"));
    if (myQueriesFolder != null)
    {
        string queryName = "<QUERY_NAME>";
        // See if our 'REST Sample' query already exists under 'My Queries' folder.
        QueryHierarchyItem newBugsQuery = null;
        if (myQueriesFolder.Children != null)
        {
            newBugsQuery = myQueriesFolder.Children.FirstOrDefault(qhi => qhi.Name.Equals(queryName));
        }
        var queryResult = witClient.QueryByIdAsync(newBugsQuery.Id).Result;
        // need to get the list of our work item id's and put them into an array
        int[] workItemIds = queryResult.WorkItems.Select<WorkItemReference, int>(wif => { return wif.Id; }).ToArray();
        // build a list of the fields we want to see
        string[] fields = new []
            {
                "System.Id",
                "System.Title",
                "System.State",
                "System.Description"
            };

        IEnumerable<WorkItemPresentation> workItems = witClient.GetWorkItemsAsync(workItemIds, fields, queryResult.AsOf).Result.Select(w => new WorkItemPresentation(){Id=w.Fields["System.Id"].ToString(), 
                                                                                                                                                                        Name=w.Fields["System.Title"].ToString(), 
                                                                                                                                                                        State=w.Fields["System.State"].ToString(), 
                                                                                                                                                                        Description=w.Fields.ContainsKey("System.Description") ? w.Fields["System.Description"].ToString() : "" });

        display(workItems);
    }
Enter fullscreen mode Exit fullscreen mode

Modify Output Rendering

In order to manipulate the rendering, we need to define a special Formatter for our type.

using static Microsoft.DotNet.Interactive.Formatting.PocketViewTags;
using Microsoft.DotNet.Interactive.Formatting;

public string Clean(string content){
    return content.Replace("[", " ").Replace("]", " ").Replace("(", " ").Replace(")", " ");
}

Formatter.ResetToDefault();
Formatter.Register<IEnumerable<WorkItemPresentation>>((workItems, writer) => 
{
    foreach (var workItemPresentation in workItems)
    {
        writer.WriteLine("<br/>");
        writer.WriteLine("---");
        writer.WriteLine("<br/>");
        writer.WriteLine("<br/>");
        writer.WriteLine($"# {Clean(workItemPresentation.Name)}");        
        writer.WriteLine("<br/>");
        writer.WriteLine("<br/>");
        writer.WriteLine($"{Clean(!string.IsNullOrEmpty(workItemPresentation.Description) ? workItemPresentation.Description :  workItemPresentation.Name)}");
        writer.WriteLine("<br/>");
    }
}, mimeType: "text/html");
Enter fullscreen mode Exit fullscreen mode

See the docs for formatting.

Interesting post by Lady Nagaga on how to pass data from one kernel to another.

Hope this helps !

Image of Docusign

Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay