<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Julian Atanasoae</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/julianatanasoae/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/julianatanasoae/feed.xml</link>
    <description>Ramblings about Azure, OSS, .NET and more</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:13:47 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>How to build a VSTS assistant - The Life and Times of a Kanban bot - Part 3</title>
      <link>https://docs.microsoft.com/archive/blogs/julianatanasoae/how-to-build-a-vsts-assistant-the-life-and-times-of-a-kanban-bot-part-3</link>
      <pubDate>Tue, 28 Aug 2018 00:59:26 GMT</pubDate>
      <dc:creator><![CDATA[Julian Atanasoae]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/julianatanasoae/?p=275</guid>
      <description><![CDATA[In the previous post (Part 2) we learned how to expand our bot functionality and be able to create...]]></description>
      <content:encoded><![CDATA[In the previous post (<a href="https://blogs.msdn.microsoft.com/julianatanasoae/2018/03/05/how-to-build-a-vsts-assistant-the-life-and-times-of-a-kanban-bot-part-2/">Part 2</a>) we learned how to expand our bot functionality and be able to create work items using our voice.

Today, we will try to mark a work item as done or as "Closed" in VSTS.

This problem is split in two parts, which corresponds to the two APIs we will be using:
<ol>
 	<li>Searching for a specific work item by title - Fetch Work Item Search Results - <a href="https://docs.microsoft.com/en-us/rest/api/vsts/search/work%20item%20search%20results/fetch%20work%20item%20search%20results?view=vsts-rest-4.1">https://docs.microsoft.com/en-us/rest/api/vsts/search/work%20item%20search%20results/fetch%20work%20item%20search%20results?view=vsts-rest-4.1</a></li>
 	<li>Marking it as done - Update Work Item and change the state - <a href="https://docs.microsoft.com/en-us/rest/api/vsts/wit/work%20items/update?view=vsts-rest-4.1">https://docs.microsoft.com/en-us/rest/api/vsts/wit/work%20items/update?view=vsts-rest-4.1</a></li>
</ol>
Taking a look at the ALM Search REST API documentation we will see that we'll have to build a request that looks like the one below:
<pre><code>POST https://{accountName}.almsearch.visualstudio.com/_apis/search/workitemsearchresults?api-version=4.1-preview.1</code></pre>
Notice that for the search functionality, we have to call the <em>almsearch</em> endpoint and not the usual project endpoint. (API versions may differ too, so make sure you check the link above for updates to the API documentation).

The body of the request looks like below:
<pre><code>
{
  "searchText": "&lt;text to search&gt;",
  "$skip": 0,
  "$top": 1,
  "filters": {
     "System.AssignedTo": [
          "John Doe &lt;john@contoso.com&gt;"
    ]
  },
  "$orderBy": [
    {
      "field": "system.id",
      "sortOrder": "ASC"
    }
  ],
  "includeFacets": true
}
</code></pre>
and you will get a JSON back with a count of the work items found - in our sample we will only going to take the first result (nobody really has identically titled work items active at the same time - Agile wizards, feel free to jump on and start a debate on this :D ), and just grab the <em>system.id</em> field. Then, after we have our work item ID returned, it means that we found it, and we will call the second API in order to update it.

The second API is the Update Work Item one, and it looks like below:
<pre><code>PATCH https://{accountName}.visualstudio.com/{project}/_apis/wit/workitems/{id}?api-version=4.1
</code></pre>
with the following request body:
<pre><code>
[
  {
    "op": "add",
    "path": "/fields/System.State",
    "value": "Closed"
  }
]
</code></pre>
That's it. Check out the full code here: <a href="https://github.com/julianatanasoae/VSTSFunctionApp/blob/master/MyVSTSFunction/UpdateWorkItem.cs">https://github.com/julianatanasoae/VSTSFunctionApp/blob/master/MyVSTSFunction/UpdateWorkItem.cs</a>

Just like in part 2, we have to set up some training phrases in Dialogflow:

<img src="https://msdnshared.blob.core.windows.net/media/2018/08/training.png" alt="" width="740" height="517" class="alignnone size-full wp-image-285" />

And the same as before, in the Action and parameters section:

<img src="https://msdnshared.blob.core.windows.net/media/2018/08/parameters-1024x300.png" alt="" width="1024" height="300" class="alignnone size-large wp-image-295" />

That's pretty much it.

Let's take a look and see how it works.

This is how my work item board looks like right now:

<img src="https://msdnshared.blob.core.windows.net/media/2018/08/workitems.png" alt="" width="857" height="376" class="alignnone size-full wp-image-305" />

In the Dialogflow test console, I will type "mark work item define user requirements as done" to close that work item. <img src="https://msdnshared.blob.core.windows.net/media/2018/08/dialogflow.png" alt="" width="541" height="1014" class="alignnone size-large wp-image-315" />

&nbsp;

As you can see, it found my work item, and it marked it as done.

Let's refresh the VSTS dashboard and see what happened:

<img src="https://msdnshared.blob.core.windows.net/media/2018/08/done.png" alt="" width="897" height="362" class="alignnone size-full wp-image-325" />

That particular work item doesn't show up anymore. If we change the view from <em>Assigned to me</em> to <em>Recently completed</em> we'll see the following:

<img src="https://msdnshared.blob.core.windows.net/media/2018/08/completed.png" alt="" width="911" height="305" class="alignnone size-full wp-image-335" />

It worked.

This is how you can mark work items as done by using the VSTS API. You can extend it and call more actions, assign tasks to someone else, assign tasks to a specific iteration (after finding a matching iteration), creating new iterations etc. Now, because this bot is configured to take an authorization header, I tailored it to only show work items that are assigned to the current user by using the <em>System.AssignedTo</em> parameter, as you don't want to screw up some other work items from your team members. :)

There are lots of improvements that can be done here, but it's shaping up as a nice prototype.

In the next post we will be talking about cross-platform stuff, as we'll shift our attention towards other assistants.

Happy Kanban-ing!

I'd love to hear your feedback on this.]]></content:encoded>
    </item>
    <item>
      <title>How to build a VSTS assistant - The Life and Times of a Kanban bot - Part 2</title>
      <link>https://docs.microsoft.com/archive/blogs/julianatanasoae/how-to-build-a-vsts-assistant-the-life-and-times-of-a-kanban-bot-part-2</link>
      <pubDate>Mon, 05 Mar 2018 10:04:52 GMT</pubDate>
      <dc:creator><![CDATA[Julian Atanasoae]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/julianatanasoae/?p=195</guid>
      <description><![CDATA[In the previous post (Part 1) we learned how we can build a bot that can read out the assigned tasks...]]></description>
      <content:encoded><![CDATA[In the previous post (<a href="https://blogs.msdn.microsoft.com/julianatanasoae/2017/12/28/how-to-build-a-vsts-assistant-the-life-and-times-of-a-kanban-bot-part-1/">Part 1</a>) we learned how we can build a bot that can read out the assigned tasks for a specific user. Today we're continuing our series by learning how to use the VSTS API and Dialogflow to create work items.

Taking a look on the VSTS REST API documentation <a href="https://www.visualstudio.com/en-us/docs/integrate/api/wit/work-items#with-a-work-item-link">https://www.visualstudio.com/en-us/docs/integrate/api/wit/work-items#with-a-work-item-link</a> we'll see that we have to build a request that looks like the one below:
<pre><code>PATCH https://{instance}/DefaultCollection/{project}/_apis/wit/workitems/${workItemTypeName}?api-version={version}</code></pre>
<pre><code>Content-Type: application/json-patch+json</code></pre>
Here's a sample:

<img src="https://msdnshared.blob.core.windows.net/media/2018/03/sample-1024x380.png" alt="" width="1024" height="380" class="alignnone size-large wp-image-205" />

For demo purposes, I decided to add some additional fields that will also add the task to Iteration 1 of my project and assigns it to my user.

The JSON array will look like the one below:
<pre><code>
[
{
"op": "add",
"path": "/fields/System.Title",
"value": "JavaScript implementation for Microsoft Account"
},
{
"op": "add",
"path": "/fields/System.AreaPath",
"value": "MyFirstProject"
},
{
"op": "add",
"path": "/fields/System.IterationPath",
"value": "MyFirstProject\\Iteration 1"
},
{
"op": "add",
"path": "/fields/System.AssignedTo",
"value": "John Doe &lt;john@jutestorg.onmicrosoft.com&gt;"
}
]
</code></pre>
I went ahead and created a new function in my Azure Function App, called CreateWorkItem. You can find the full code on GitHub - <a href="https://github.com/julianatanasoae/VSTSFunctionApp/blob/master/MyVSTSFunction/CreateWorkItem.cs">https://github.com/julianatanasoae/VSTSFunctionApp/blob/master/MyVSTSFunction/CreateWorkItem.cs</a>

There was some additional work needed in order for HttpClient to be able to do PATCH requests, so I went ahead and created an HttpClientExtension that used this method. It's used like this:
<pre><code>
var queryHttpResponseMessage = client.PatchAsync(url, content).Result;
if (queryHttpResponseMessage.IsSuccessStatusCode)
{
var response = "Work item created";
speechJson = "{ \"speech\": \"" + response + "\" }";
}
</code></pre>
Dialogflow will pass a JSON of the following form to our function:
<pre><code>
{
  "result": {
    "parameters": {
       "workItemTitle": "something"
    }
  }
}
</code></pre>
So we have to make sure that in the Run function of the app, we're reading the workItemTitle correctly:
<pre><code>
dynamic data = await req.Content.ReadAsAsync&lt;object&gt;();
var workItemTitle = data?.result.parameters.workItemTitle.ToString();</code></pre>
That's it. The function can be published now and read from Dialogflow.

In the Dialogflow console, we first have to create an Entity called "workItemTitle", to be able to give it as a parameter on an intent.

After this, when creating the "Create Work Item" intent, we have to give it training phrases, as shown below:

<img src="https://msdnshared.blob.core.windows.net/media/2018/03/training.png" alt="" width="684" height="310" class="alignnone size-full wp-image-215" />

Notice that the entities are highlighted with yellow. In order for your bot to be trained correctly, you have to manually select the words at the end with the mouse and pick the corresponding entity (in our case, workItemTitle).

In the Action and parameters section, we will need to create an action and give the workItemTitle as a parameter.

<img src="https://msdnshared.blob.core.windows.net/media/2018/03/parameters.png" alt="" width="697" height="337" class="alignnone size-full wp-image-225" />

That's it. Let's take a look and see if it works!

This is how my Kanban board looks like right now:

<img src="https://msdnshared.blob.core.windows.net/media/2018/03/kanbanbefore.png" alt="" width="708" height="424" class="alignnone size-full wp-image-235" />

In the Dialogflow test console, I will type "create a work item with the title MSDN rocks":

<img src="https://msdnshared.blob.core.windows.net/media/2018/03/dialogflow.png" alt="" width="358" height="574" class="alignnone size-full wp-image-245" />

As you can see, it identified my "Create Work Item" intent, and it also identified that I passed a parameter corresponding to the workItemTitle entity, with the value "MSDN rocks".

Fingers crossed while refreshing the VSTS work item dashboard...

<img src="https://msdnshared.blob.core.windows.net/media/2018/03/msdnrocks.png" alt="" width="710" height="491" class="alignnone size-full wp-image-255" />

It works!

This is an example of how you can use the VSTS API to create work items. Surely, there's a lot of things that can be done better: logging in to the Azure AD tenant so I can get the proper user name, not hardcoding it in a JSON object, as well as implementing prompts for things such as Iteration Path, parent user story (as it currently shows under 'Unparented'), and more. It's a nice start, though.

Let me know in the comments below if you like this or if you have ideas on contributing to the bot functionality.

Happy Kanban-ing!]]></content:encoded>
    </item>
    <item>
      <title>How to install custom dependencies on Azure Functions on Linux</title>
      <link>https://docs.microsoft.com/archive/blogs/julianatanasoae/how-to-install-custom-dependencies-on-azure-functions-on-linux</link>
      <pubDate>Sun, 14 Jan 2018 17:06:56 GMT</pubDate>
      <dc:creator><![CDATA[Julian Atanasoae]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/julianatanasoae/?p=145</guid>
      <description><![CDATA[I recently encountered an issue with Azure Functions on Linux, which is in preview right now...]]></description>
      <content:encoded><![CDATA[I recently encountered an issue with Azure Functions on Linux, which is in preview right now (<a href="https://blogs.msdn.microsoft.com/appserviceteam/2017/11/15/functions-on-linux-preview/">https://blogs.msdn.microsoft.com/appserviceteam/2017/11/15/functions-on-linux-preview/</a>) while trying to use an open-source library that had some additional dependencies to be able to run successfully, namely wkhtmltopdf - <a href="https://github.com/wkhtmltopdf/wkhtmltopdf">https://github.com/wkhtmltopdf/wkhtmltopdf</a>.

While trying to set up a basic function that was using this library, I got the following error:

<img src="https://msdnshared.blob.core.windows.net/media/2018/01/log.png" alt="" class="alignnone size-full wp-image-155" width="926" height="318" />

<code>./wkhtmltopdf: error while loading shared libraries: libXrender.so.1: cannot open shared object file: No such file or directory</code>

Doing a search on the project's GitHub page, I found this issue: <a href="https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1515">https://github.com/wkhtmltopdf/wkhtmltopdf/issues/1515</a>

and I figured that there's a number of packages that the Linux image needs to have in order for this library to work correctly.

Because Azure Functions on Linux is built on containers, you can host your functions in your own custom container, as described here: <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-function-linux-custom-image">https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-function-linux-custom-image</a>

Usually, Function Apps on Linux are based on this image here: <a href="https://github.com/Azure-Samples/functions-linux-custom-image/blob/master/Dockerfile">https://github.com/Azure-Samples/functions-linux-custom-image/blob/master/Dockerfile</a>

At the bottom of the Dockerfile, you can add a command that installs the packages you need for your function to work, for example:
<pre><code>RUN apt-get update \
&amp;&amp; apt-get install -y --no-install-recommends openssl build-essential libssl-dev libxrender-dev git-core libx11-dev libxext-dev libfontconfig1-dev libfreetype6-dev fontconfig
</code></pre>
After you have the Dockerfile ready, in the folder that contains the Dockerfile, paste the host.json file and the folder containing your function, as shown below:

<img src="https://msdnshared.blob.core.windows.net/media/2018/01/files.png" alt="" class="alignnone size-full wp-image-165" width="740" height="147" />

and then build the image, by following the steps in the how-to article on Microsoft Docs mentioned above.

After building the image and publishing it to a Docker Hub, you should create a new function app by using Azure CLI and giving the custom container as a parameter.

First, you should create a new Linux App Service plan:
<pre><code>az appservice plan create --name 'plan_name' --resource-group 'resource_group_name' --sku S1 --is-linux</code></pre>
and then create the Function App, based on the custom image you published, by running the following command:
<pre><code>az functionapp create --name 'app_name' --storage-account  'storage_name'  --resource-group 'resource_group_name' \
--plan 'plan_name' --deployment-container-image-name 'docker-id/docker-image-name:docker-image-tag'</code></pre>
where
<pre><code>'docker-id'</code></pre>
is the name of your Docker Hub account.

After this, the portal will look like the one below:

<img src="https://msdnshared.blob.core.windows.net/media/2018/01/readonly-1024x460.jpg" alt="" class="alignnone wp-image-175" width="809" height="363" />

The functions will be Read Only, so you will not be able to edit them or create new ones directly from the portal, you would have to do that from your filesystem and create new container images and publish them.

As you can see, the function is working and I got a 200 OK response and a PDF output, as shown in the image above.

Have fun with custom containers!]]></content:encoded>
    </item>
    <item>
      <title>How to build a VSTS assistant - The Life and Times of a Kanban bot - Part 1</title>
      <link>https://docs.microsoft.com/archive/blogs/julianatanasoae/how-to-build-a-vsts-assistant-the-life-and-times-of-a-kanban-bot-part-1</link>
      <pubDate>Thu, 28 Dec 2017 23:21:21 GMT</pubDate>
      <dc:creator><![CDATA[Julian Atanasoae]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/julianatanasoae/?p=65</guid>
      <description><![CDATA[Greetings everyone,

If you're working as a developer, chances are you've heard about Visual...]]></description>
      <content:encoded><![CDATA[Greetings everyone,

If you're working as a developer, chances are you've heard about Visual Studio Team Services, and you're a master of Kanban boards, iterations, work items, code management, continuous integration pipelines, and you probably know the <a href="https://agilemanifesto.org/">Agile manifesto</a> by heart... ;)

We live in an era of thousands of tools and workflows that we have to use and pay attention to every time (or maybe I could do better at organizing myself...) Whatever the case may be, I always wondered how it would be like to have an assistant manage tasks for me.

If you relate to anything that I wrote above, then this article is for you. You probably even had moments like these:

[caption id="" align="alignnone" width="406"]<img src="https://i1.wp.com/media.boingboing.net/wp-content/uploads/2013/04/yBa1OGm1.jpg?w=970" class="bordered" alt="" width="406" height="544" /> Source: https://boingboing.net/2013/04/07/great-do-not-disturb-statu.html[/caption]

and you are constantly looking for solutions to automate tasks and make you stay in "the zone".

Having to alt-tab constantly between Visual Studio and the task board all the time may make you go out of focus. :) And just before a project manager or a scrum master wants to fill up the comment section with a lot of arguments against this, remember - this is supposed to be a fun post, and <em>real world may differ significantly</em><em>.
</em>

As we live in a world that's getting filled up with voice assistants and artificial intelligence, I thought of building a bot that can manage tasks from the Visual Studio Team Services board for me. This will be a multi-part series where I will explain what I did and show you how you can build your own assistant too.

Visual Studio Team Services has a REST API which you can use to do lots of tasks, such as managing work items, kicking off builds, doing test runs, adding widgets to dashboards, managing repositories, creating release definitions and many more: <a href="https://www.visualstudio.com/en-us/docs/integrate/api/overview">REST API Overview for Visual Studio Team Services and Team Foundation Server</a>

I logged in to a VSTS test project and added a sample iteration and some tasks:

<img src="https://msdnshared.blob.core.windows.net/media/2017/12/kanban.png" alt="" class="alignnone size-full wp-image-85" width="960" height="242" />

Next up, I decided to build an Azure Function, that would query the VSTS REST API and expose a list of my tasks.

First, I had to create an Azure AD Application that would have permissions for Visual Studio Team Services, and use ADAL to authenticate against it. A quick search would lead you to a very handy repository on GitHub: <a href="https://github.com/Microsoft/vsts-auth-samples">https://github.com/Microsoft/vsts-auth-samples</a>

Because I would write C# code, I decided to look over the Managed Client sample, and quickly figured out how to configure the app, delegate permissoins, install and configure ADAL and get an authentication token. At the top, you would have to define a series of constant strings:
<pre><code>internal const string VstsCollectionUrl = "https://account.visualstudio.com"; //change to the URL of your VSTS account; NOTE: This must use HTTPS
internal const string ClientId = "&lt;clientId&gt;"; //update this with your Application ID
internal const string Username = "&lt;username&gt;"; //This is your AAD username (user@tenant.onmicrosoft.com or user@domain.com if you use custom domains.)
internal const string Password = "&lt;password&gt;"; // This is your AAD password.
internal const string Project = "&lt;project&gt;"; // This is the name of your project</code></pre>
and then set up an authenticationContext and login to the Azure AD app in order to get the accessToken:
<pre><code>private static AuthenticationContext GetAuthenticationContext(string tenant)
{
     AuthenticationContext ctx;
     if (tenant != null)
        ctx = new AuthenticationContext("https://login.microsoftonline.com/" + tenant);
     else
     {
        ctx = new AuthenticationContext("https://login.windows.net/common");
        if (ctx.TokenCache.Count &gt; 0)
        {
            var homeTenant = ctx.TokenCache.ReadItems().First().TenantId;
            ctx = new AuthenticationContext("https://login.microsoftonline.com/" + homeTenant);
        }
     }
     return ctx;
}</code></pre>
The main function looks like this:
<pre><code>
        [FunctionName("GetWorkItemList")]
        public static async Task Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
        {
            log.Info("C# HTTP trigger function processed a request.");

            var ctx = GetAuthenticationContext(null);
            try
            {
                var adalCredential = new UserPasswordCredential(Username, Password);

                var result = ctx.AcquireTokenAsync(VstsResourceId, ClientId, adalCredential).Result;
                var bearerAuthHeader = new AuthenticationHeaderValue("Bearer", result.AccessToken);
                var speechData = GetWorkItemsByQuery(bearerAuthHeader);
                return new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent(speechData, Encoding.UTF8, "application/json")
                };
            }
            catch (Exception ex)
            {
                log.Error("Something went wrong.");
                log.Error("Message: " + ex.Message + "\n");
                return req.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error!");
            }
        }</code></pre>
A pretty basic function - I'm acquiring a token, constructing an authorization header and calling a function called GetWorkItemsByQuery, where I pass that authorization header, so I can get the list in a JSON format.

Here are some examples on how you can manipulate work items: <a href="https://www.visualstudio.com/en-us/docs/integrate/api/wit/work-items">https://www.visualstudio.com/en-us/docs/integrate/api/wit/work-items</a>

Looking over the samples here: <a href="https://www.visualstudio.com/en-us/docs/integrate/api/wit/samples">https://www.visualstudio.com/en-us/docs/integrate/api/wit/samples</a>

I decided to make a function that would retrieve a list of the items assigned to me:
<pre><code>
public static string GetWorkItemsByQuery(AuthenticationHeaderValue authHeader)
        {
            const string path = "Shared Queries/My Tasks"; //path to the query   
            var speechJson = "{ \"speech\": \"Sorry, an error occurred.\" }";
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(VstsCollectionUrl);
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
                client.DefaultRequestHeaders.Add("User-Agent", "VstsRestApi");
                client.DefaultRequestHeaders.Add("X-TFS-FedAuthRedirect", "Suppress");
                client.DefaultRequestHeaders.Authorization = authHeader;

                //if you already know the query id, then you can skip this step
                var queryHttpResponseMessage = client.GetAsync(Project + "/_apis/wit/queries/" + path + "?api-version=2.2").Result;

                if (queryHttpResponseMessage.IsSuccessStatusCode)
                {
                    //bind the response content to the queryResult object
                    var queryResult = queryHttpResponseMessage.Content.ReadAsAsync().Result;
                    var queryId = queryResult.id;

                    //using the queryId in the url, we can execute the query
                    var httpResponseMessage = client.GetAsync(Project + "/_apis/wit/wiql/" + queryId + "?api-version=2.2").Result;

                    if (httpResponseMessage.IsSuccessStatusCode)
                    {
                        var workItemQueryResult = httpResponseMessage.Content.ReadAsAsync().Result;

                        //now that we have a bunch of work items, build a list of id's so we can get details
                        var builder = new System.Text.StringBuilder();
                        foreach (var item in workItemQueryResult.workItems)
                        {
                            builder.Append(item.id.ToString()).Append(",");
                        }

                        //clean up string of id's
                        var ids = builder.ToString().TrimEnd(',');

                        var getWorkItemsHttpResponse = client.GetAsync("_apis/wit/workitems?ids=" + ids + "&amp;fields=System.Id,System.Title,System.State&amp;asOf=" + workItemQueryResult.asOf + "&amp;api-version=2.2").Result;

                        if (getWorkItemsHttpResponse.IsSuccessStatusCode)
                        {
                            var result = getWorkItemsHttpResponse.Content.ReadAsStringAsync().Result;

                            // the work item list is exposed as a JSON object
                            var myWorkItemList = JsonConvert.DeserializeObject(result);
                            
                            // I iterate through the list of work items and get each title and state, which I concatenate so the result can be 'spoken'
                            var response = myWorkItemList.value.Aggregate("", (current, item) =&gt; current + (item.fields.SystemTitle + " - " + item.fields.SystemState + ' '));

                            // Google Assistant-specific syntax
                            speechJson = "{ \"speech\": \"" + response + "\" }";
                        }
                    }
                }
            }
            return speechJson;
        }
</code></pre>
As you can read through the code, I'm using a path to a specific query, as VSTS provides lots of queries. In my case, it would be "My Tasks".

Then, by using the authorization header I got earlier, I'm querying the API, and as the list is returned as a JSON object, I'm deserializing it - hint: you can use <a href="https://json2csharp.com/">https://json2csharp.com/</a> to generate classes.

Because VSTS tasks have properties such as System.Id, System.State and System.Title, you must define the properties in the Fields class as below:
<pre><code>
public class Fields
    {
        [JsonProperty("System.Id")]
        public int SystemId { get; set; }

        [JsonProperty("System.State")]
        public string SystemState { get; set; }

        [JsonProperty("System.Title")]
        public string SystemTitle { get; set; }
    }
</code></pre>
After this operation, a "speech" JSON must be constructed, because you would want these to be spoken out loud by an assistant.

You can use Cortana, Alexa, Google Assistant, Siri or anything else you want, as long as it has a speech API implemented. Make sure you look the documentation up for what you want to use, and build the JSON object as described in those docs.

In my case, I used a Google Home Mini, so I had to look up the syntax for Dialogflow: <a href="https://dialogflow.com/docs/fulfillment">https://dialogflow.com/docs/fulfillment</a>

Done - ready to be consumed. Here's how the output looked like:
<pre>{ "speech": "Create the DB diagram - Active Create classes from DB tables - New " }</pre>
I went ahead and published it to Azure Functions, and copied the Function URL: <a href="https://yourfunctionappname.azurewebsites.net/api/GetWorkItemList?code=&lt;token&gt;">https://yourfunctionappname.azurewebsites.net/api/GetWorkItemList?code=&lt;token&gt;</a>

You can get this URL from the Functions portal, by clicking on <em>Get Function URL</em>:

<img src="https://msdnshared.blob.core.windows.net/media/2017/12/GetFunctionURL-1024x477.png" alt="" class="alignnone wp-image-95" width="683" height="318" />

And then I went to the <a href="https://console.dialogflow.com/">Dialogflow console</a> and created the bot. In the Fulfillment section, I had to paste the URL of the function in the Webhook field:

<img src="https://msdnshared.blob.core.windows.net/media/2017/12/fulfillment-1024x403.png" alt="" class="alignnone wp-image-96" width="709" height="279" />

And at the end, I created an Intent, so that when it hears "show me my work items", it triggers the Webhook we configured, plays the response it receives, and ends the conversation:

<img src="https://msdnshared.blob.core.windows.net/media/2017/12/intent-1024x427.png" alt="" class="alignnone size-large wp-image-115" width="1024" height="427" />

<img src="https://msdnshared.blob.core.windows.net/media/2017/12/usewebhook.png" alt="" class="alignnone size-full wp-image-105" width="842" height="337" />

That's it.

Let's see it in action.

[video width="1920" height="1080" mp4="https://msdnshared.blob.core.windows.net/media/2017/12/20171228_224211.mp4"][/video]

The code for the Function App can be found here: <a href="https://github.com/julianatanasoae/VSTSFunctionApp">https://github.com/julianatanasoae/VSTSFunctionApp</a>

That's it for Part 1 of this series. In the next article, we'll see how we can add work items, assign them to a user, change the state and more.

Cheers!]]></content:encoded>
    </item>
    <item>
      <title>Hello, MSDN World!</title>
      <link>https://docs.microsoft.com/archive/blogs/julianatanasoae/hello-msdn-world</link>
      <pubDate>Tue, 31 Oct 2017 17:50:40 GMT</pubDate>
      <dc:creator><![CDATA[Julian Atanasoae]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/julianatanasoae/?p=25</guid>
      <description><![CDATA[Greetings everyone,

My name is Julian Atanasoae, and I work as a Developer Support Engineer on...]]></description>
      <content:encoded><![CDATA[Greetings everyone,

My name is Julian Atanasoae, and I work as a Developer Support Engineer on the Azure App Services team. My main focus is troubleshooting customer apps - not only .NET, but also open-source technologies (OSS) - .NET Core, Node.js, Java, PHP, Python and more. The general availability of App Service on Linux and Web App for Containers endorses the fact that we are now language-agnostic, and we are committed to provide great customer experiences.

A little bit about me - I started my career some years ago by becoming the youngest Romanian developer with a published Windows Store app (I was 16 at the time), slowly shifting my focus to cloud technologies (Xamarin is still close to my heart, though).

This blog will be focused on Azure, open-source, debugging, weekend projects &amp; more. I'm very excited to see where this journey will take us.

If you want to get in touch, follow me on Twitter - @julianatanasoae.]]></content:encoded>
    </item>
  </channel>
</rss>