Angular 7 Guard Service and Unit Tests

I’m fairly new to Angular 7, (or indeed Angular 2+, having spent the last couple of years doing AngularJS development.

So, in Angular, I’ve created a Guard service which all made sense and was fairly straightforward, however, I found that writing the unit tests was much harder, finding that none of the examples on StackOverflow or on other sites seemed to work with Angular 7.

The code is here:

The guard service

The guard will check whether a user is logged in, and if a claim is passed in, will check whether the user has the claim.

The checkLoggedIn method, first checks the authentication service to see if the user is logged in. If the user is logged in it simply returns true. If the user is not logged in, it saves the desired URL to the authentication service to allow for redirection once the user is logged in, then it navigates to the login page.

The checkClaim method checks whether the currentUser object in the authorisation service has the claim in the claims list.

The unit test

First, we set up a mock ActivedRouteSnapshot and a mock RouterStateSnapshot. In the MockActivatedRouteSnapshot we have the data object that will hold the expectedClaim.


Then we set up the describe block, providing the guard and the ActivedRouteSnapshot.

Logged Out Test

Next the first test, checking that you can’t get to a route that requires authentication if you’re logged out.

So in this test, we’re setting a mock authentication service, that returns false, from checkLoggedIn, and also with the spyOnProperty, we’re telling it to return the expectedClaim = ‘policy’ in the data value of the route.

Logged In Test

Next, checking whether you can get to a route if you’re logged in:

In this test, w’re returning true for the checkLoggedin method, but not requiring a claim.

No claim test

Next, checking that you can’t get to a route that requires a claim you don’t have.

In this test, we’re setting up the authentication service to return true for logged in, and setting the route to require the expectedClaim of ‘policy’, but we’re not adding the that claim to the user.

Valid claim test

Finally, check that you can get to a route that you have a valid claim for.

In this test, we’ve set up the currentUser object in the authentication service to have a claims property, that contains a claim of ‘policy’.

Adding Swagger to a C# WebApi

About Swagger

Swagger (https://swagger.io/) is a free open-source tool that enables easy documentation and testing of APIs. It is language agnostic.

Swashbuckle (https://github.com/domaindrivendev/Swashbuckle) is a .Net specific tool that adds Swagger to a .Net Web Api.

Installation

Install from Nuget

Install-Package Swashbuckle

Register Swagger

The Swashbuckle install should create a SwaggerConfig.cs file in the App_start Directory. This will allow you to configure Swagger in various ways.

Enable XML document generation

Open the project properties for your WebApi project, select the build tab and change the setting below:

Annotation 2019-02-20 111835

Update swagger config to use the XML comments

In the SwaggerConfig.cs file uncomment the line below:

c.IncludeXmlComments(GetXmlCommentsPath());

and add the method :

private static string GetXmlCommentsPath()
{
   var filepath = System.String.Format(@"{0}\bin\documentation.XML", System.AppDomain.CurrentDomain.BaseDirectory);
   return filepath;
}

Adding XML comments to code

On your controller and individual endpoints add the /// triple slash comments, for example

///
/// Search returns a paged, filterd list of events
///

///filters for the search ///number of entries to return per page ///page number to return ///name of column to sort by ///direction of sort /// A list of Events /// Success /// No Data Found /// Internal Server Error [Route(“Search”)] [HttpGet] [SwaggerResponse(200)] [ResponseType(typeof(IQueryable))] public IHttpActionResult GetList([FromUri] EventArguments parameters, [FromUri] int pageSize = 25, [FromUri] int pageNumber = 1, [FromUri] string sortColumn = “Id”, [FromUri] bool sortForward = true) { … }

 

Launching .EXE applications from a browser

My client has an Angular intranet portal for their main c# WinForm application. They have a requirement to launch the main application from the portal website.

There is a fairly easy way to go about this in Windows although it does require adding an entry to the registry, to set up a URI Scheme. Here is an example to load notepad.exe

HKEY_CLASSES_ROOT
   jmNotepad
      (Default) = "URL:jmNotepad Protocol"
      URL Protocol = ""
      DefaultIcon
         (Default) = "notepad.exe,1"
      shell
         open
            command
               (Default) = "C:\windows\notepad.exe"

In the web page, add a simple link:

<a href="jmNotepad:">Launch Notepad</a>

The first time it’s launched you get a warning, which you can click to ignore subsequently.

WebApi2, CORS & HTTP 401 Unauthorized error

I got caught out with this issue again today, and it took me ages to find/remember the fix. Whilst using VS2013 to debug a website & web service running on separate ports on localhost, I kept getting a 401 error. I checked and rechecked that I had CORS set up properly, but the error just wouldn’t go away.

At last I remembered to set the default credentials on the WebClient instance:

        public User GetUser(string userName)
        {
            User model = null;
            string json = string.Empty;
            using (WebClient client = new WebClient())
            {
                client.UseDefaultCredentials = true;

                try
                {
                    string url = this.Url + "/"  + HttpUtility.UrlEncode(userName);

                    json = client.DownloadString(url);
                }
                catch (WebException)
                {
                    json = string.Empty;
                }
            }
            if (json.Length > 0)
            {
                model = JsonConvert.DeserializeObject(json);
            }
            return model;
        }
Angular.js

Login redirection using AngularJS & UI-Router

Angular.js

Angular

When a user follows a link on an email to the site, but needs to login before they can view the page, you need to remember the requested page and return to it after a successful login.

In the following code examples, I’m using an AuthenticationService which handles user login, and stores authentication state.

First on the landing page, you need to store the current router state and the params needed to get back to the specific page.

if (!AuthenticationService.isAuthenticated) {
$cookies.loginDestination = $state.$current.name;
$cookies.loginParams = { id: $stateParams.id};
$state.transitionTo("Login");
};

In the AuthenticationService, after a successful login:

var loginDestination = $cookies.loginDestination || '/';
var loginParams = $cookies.loginParams || null;
$cookies.loginDestination = null;
$cookies.loginParams = null;
$state.transitionTo(loginDestination, loginParams);

Javascript error in *.min.js.map files

I’ve been getting an JavaScript error:

JavaScript critical error at line 2, column 14 in http://localhost:53885/Scripts/jquery.unobtrusive-ajax.min.js.map\n\nSCRIPT1004: Expected ‘;’

This has happened in a few different files, I tried regenerating the *.min.js and *.min.js.map files, but it just wouldn’t go away.

Eventually with a bit of searching Ifound the cause of the problem, Asp.Net MVC Bundles.

Our code was:

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
            "~/Scripts/jquery.unobtrusive*",
            "~/Scripts/jquery.validate*"
            ));

and we changed it to:

bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
            "~/Scripts/jquery.unobtrusive-{version}.js",
            "~/Scripts/jquery.validate-{version}.js"
            ));

This stops the bundle loading the map files.

Web Dev Gotchas–Personal

This post is just for me, because I’ve forgotten more than once.

1. Until I update my scaffolding to work with VS2103, it doesn’t. Create the projects in Vs2012

2. WebApi – Json camelCase and serialising child objects :

var jsonFormatter = GlobalConfiguration.Configuration.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
jsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

3. Repositories – including child objects

using System.Collections.Generic;
using System.Linq;
using System.Data.Objects;
public override ICollection<Book> GetAll()
{     BookReviewsContext context = (BookReviewsContext)DataContext;     var books = context         .Books         .Include("Authors")         .Where(x => !x.IsDeleted)         .ToList<Book>();     return books;
 
}