VSTS – Package Manager with NuGet

As our project has grown we have identified a number of occurrences where the same assembly is being used across a number of different branches. To improve the management of this single assembly I have been using the VSTS Package Manger to create a NuGet package that we can consume within these projects. This post is about my experience of using PM for the first time and some of the things I learned along the way. At the bottom of the post are links to URLs that I used to help me.

You can create a NuGet package on your machine using the NuGet CLI and upload it to the Package Manager manually. This is a good place to start if you want to get something into the PM and see how it looks when you pull down the package in Visual Studio etc. I found using the NuGet CLI to create the .nupkg file helped me to understand what would be required to make it part of a CI build. The NuGet CLI and VSTS NuGet Task allow you to build from either a .csproj (.vbproj) or .nuspec file. To create a .nuspec file for your project use the NuGet CLI with the spec command. The .nuspec file allows you to set the metadata for your project and include replacement tokens that can be substituted from the CLI. After creating my basic .nuspec file I can then run the following NuGet command to create my first package.

nuget pack MyCompany.MyProject.csproj -Build -Properties configuration=debug;description=My first NuGet Package;author=MyCompany -Verbosity Detailed

The -Properties argument defines what replacement tokens are substituted in the .nuspec file. If your project has an AssemblyInfo file but does not contain an entry for AssemblyCompany or AssemblyDescription you must provide these in the command line or the pack command will fail. (See the Replacement Tokens section for more information). The configuration=debug defines which project configuration will be built with the -Build flag. QUICK TIP – if you run the NuGet commands in PowerShell you need to escape the semi-colons using the opening single quote character (`).

The version number of your .nupkg will be taken from the AssemblyInformationalVersion (if available) or the AssemblyVersion entry within the AssemblyInfo file. If you are using a SharedAssemblyInfoFile (SolutionAssemblyInfoFile) to store common values then these need to be defined in the NuGet pack command -Properties as version=x.x.x.x or using the -Version argument.

At this point you can upload the completed package to the Package Manager following the instructions available here. Having created a .nupkg on my own machine the next  stage was to do this as a task of a build process. The current documentation of creating NuGet Packages during the build is a little out of date at the time of writing this article as it refers to the deprecated NuGet Packager task, however there is now a single NuGet task with the different NuGet commands as an option. When I first tried the NuGet build task the problem was making sure that the NuGet task could find the files to include in the .nupkg. To solve this problem I had to make sure the Visual Studio build task, for the .csproj, placed the files in the right location. This was achieved by adding the following MSBuild Argument /p:OutputPath=$(Build.SourcesDirectory)\MyCompany.MyProject\bin\$(BuildConfiguration) .

The .nuspec file included the source control ensures that common settings for the .nupkg are retained for each build. The NuGet Pack task allows for the setting of Pack Options for versioning, however for my build I set this to off and used the used the version specified in by the AssemblyInformationalVersion. I also specified some additional build properties (-Properties attribute) in the Advanced section.

Once the package has been created as part of the build the next stage is to include the NuGet push command task. Again, the current documentation is a little out of date as it refers to the deprecated NuGet Publisher task. To publish the package use the same NuGet task as before but this time selecting the push command. Since we have already created the Package Manager feed we just need to select this as the Target Feed in the push task. Having completed the NuGet push task, the process now has all the tasks to build the project, create a NuGet package and send it to our Package Manager. Finally, we can access the .nupkg from within Visual Studio.

URLs I have found useful on this journey

CSOM – ContentType and CAML Queries

To retrieve the ContentType of a ListItem from the ListItemCollection using the GetItems method, there are two approaches that work.

  1. If you are not specifying ViewFields in your CAML query then in your Load method you need to Include the ContentType. e.g.

clientContext.Load(listItems, items => items.Include(item => item.ContentType));

  1. If you CAML query includes ViewFields then two modifications are required. The ViewFields needs to include the ContentTypeId field and the Load method needs to include the ContentType. e.g.
<ViewFields><FieldRef Name='ContentTypeId' /></ViewFields>

clientContext.Load(listItems, items => items.Include(item => item.ContentType));

Azure (not so) Logic Apps

As with any (relatively) new Azure  app there is always pain mixed in with the pleasure. The simplicity of the Logic Apps Designer can throw in curve balls that can only be handled in the Code View.

A good example of this is the SQL -Get Row connector and what happens if the row is not found.

logic-app-get-row

The default logic here is to look for the row and then progress if the Row Id can be matched, and in the Designer view this is all handled for you. But what if you want a condition where you want to handle this failure.  The Designer allows us to put in a check for the failure, although you have to edit it in Advanced mode. The problem is that the Designer does not let you allow ‘Failed’ as a valid option.

logic-app-condition

In the Designer view this is not obvious (at the moment) so you need to switch to the Code view. The default runAfter option (for the Condition) is set to ‘Succeeded’

"runAfter" {
"Get_row" : [ "Succeeded" ]
}

So what we need to do is add ‘Failed’ as a valid option

"runAfter" {
"Get_row" : [ "Succeeded", "Failed" ]
}

The ‘Get row’ is then able to succeed by failure when no matching row is found and the Condition check will be evaluated correctly.

Google Recaptcha verification

Using the Google Recaptcha verification API should be fairly straight forward. The one thing the documentation does not make clear is how to POST to the SiteVerify endpoint. So after playing around with the various options in Postman, I have found the following code works from the server side using C#.

                HttpClient httpClient = new HttpClient();

                KeyValuePair<string, string> secret = new KeyValuePair<string, string>("secret", recaptcha.Secret);
                KeyValuePair<string, string> response = new KeyValuePair<string, string>("response", recaptcha.Response);

                List<KeyValuePair<string, string>> postData = new List<KeyValuePair<string, string>>();
                postData.Add(secret);
                postData.Add(response);

                using (var content = new FormUrlEncodedContent(postData))
                {
                    content.Headers.Clear();
                    content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

                    var recaptchaResponse = await httpClient.PostAsync(RecaptchaAPIUrl, content);

                    var returnValue = recaptchaResponse.Content.ReadAsStringAsync().Result;

                    var vr = JsonConvert.DeserializeObject<VerifiedResponse>(returnValue);

                    return this.Ok(vr);
                }

Postman – Including the Header Date

I have found Postman to be a very useful tool for testing Web Api’s, but according to Postman’s documentation, sending certain restricted headers can be an issue. Using Postman Interceptor allows you to overcome this restriction but I came across one problem when setting the restricted Date header.

I had been using the Pre-requests scripts to set the Date dynamically using environment variables, again following the documentation. However, each time I submitted the request my Date header was coming through as null. The problem turned out to be the date format as this needs to be in GMT format and fortunately the fix turned out to be relatively simple.

Javascript has a toGMTString() method that works but the method is being deprecated so the toUTCString() works just as well so the environment variable becomes

postman.setEnvironmentVariable('timestampHeader',new Date().toUTCString());

CSS Specificity

Came across this very useful tool for comparing CSS Specificity.

Specificity Calculator

This is very useful for identifying why your CSS might be overridden by competing styles in other files.

SharePoint Client – Folder and Files

If you know the relative Url of the folder within SharePoint there is a straight forward way to access the files within that folder.

using Microsoft.SharePoint.Client;
...
using (ClientContext clientContext = new ClientContext(&amp;quot;https://something.sharepoint.com/sites/mysite/&amp;quot;))
{
    Folder folder = clientContext.Web.GetFolderByServerRelativeUrl(&amp;quot;/sites/mysite/root_folder/sub_folder&amp;quot;);

    // Load the Folder and Files into the ClientContext so we can access them
    clientContext.Load(folder);
    clientContext.Load(folder.Files);
    clientContext.ExecuteQuery();

    foreach (var item in folder.Files)
    {
        // Load each file into the ClientContext to access the file information
        context.Load(item);
        context.ExecuteQuery();
    }
}