Friday 20 June 2014

A folk story wherein we shall find dates, DataAnnotations & data impedance mismatch

If you ever take a step back from what you're doing it can sometimes seem pretty abstract. Here's an example. I was looking at an issue in an app that I was supporting. The problem concerned a field which was to store a date value. Let's call it, for the sake of argument, valuation_date. (Clearly in reality the field name was entirely different... Probably.) This field was supposed to represent a specific date, like June 15th 2012 or 19th August 2014. To be clear, a date and *not* in any way, a time.

valuation_date was stored in a SQL database as a datetime. That's right a date with a time portion. I've encountered this sort of scenario many times on systems I've inherited. Although there is a date type in SQL it's pretty rarely used. I think it only shipped in SQL Server with 2008 which may go some way to explaining this. Anyway, I digress...

valuation_date was read into a field in a C# application called ValuationDate which was of type DateTime. As the name suggests this is also a date with a time portion. After a travelling through various layers of application this ended up being serialized as JSON and sent across the wire where it became a JavaScript variable by the name of valuationDate which had the type Date. Despite the deceptive name this is also, you guessed it, a date with a time portion. (Fine naming work there JavaScript!)

You can probably guess where I'm going with this... Despite our (cough) rock solid naming convention, the situation had arisen where actual datetimes had snuck in. That's right, in the wilds of production, records with valuation_dates with time components had been spotted. My mission was to hunt them, kill them and stop them reproducing...

A Primitive Problem

Dates is a sticky topic in many languages. As I mentioned, SQL Server has a date data type. C# has DateTime. If you want to operate on Dates alone then you're best off talking looking at Jon Skeet's NodaTime - though most people start with DateTime and stick with it. (After all, it's native.) As to JavaScript, well primitive-wise there's no alternative to Date - but Moment.js may help.

My point is that it is a long standing issue in the development world. We represent data in types that aren't entirely meant for the purpose that they are used. It's not just restricted to dates, numbers have a comparable story around the issue of decimals and doubles. As a result of data type issues, developers experience problems. Like the one I was facing.

An Attribute Solution

The source of the problem turned out to be the string JavaScript Date constructor in an earlier version of Internet Explorer. The fix was switching away from using the JavaScript Date constructor in favour of using Moment.js's more dependable ability to parse strings into dates. Happy days we're working once more! Some quick work to put together a SQL script to fix up the data and we have ourselves our patch!

But we didn't want to get bitten again. We wanted ourselves a little belts and braces. What do do? Hang on a minute, lads – I've got a great idea... It's ValidationAttribute time!

We whipped ourselves up an attribute that looked like this:


using System;
using System.ComponentModel.DataAnnotations;
using System.Globalization;

namespace My.Attributes
{
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
    public class DateOnlyAttribute: ValidationAttribute
    {
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            if (value != null)
            {
                if (value is DateTime)
                {
                    // Date but not Time check
                    var date = (DateTime) value;
                    if (date.TimeOfDay != TimeSpan.Zero)
                    {
                        return new ValidationResult(date.ToString("O", CultureInfo.InvariantCulture) + " is not a date - it is a date with a time", new[] { validationContext.MemberName });
                    }
                }
                else
                {
                    return new ValidationResult("DateOnlyAttribute can only be used on DateTime? and DateTime", new[] { validationContext.MemberName });
                }
            }

            return ValidationResult.Success;
        }
    }
}

This attribute does 2 things:

  1. Most importantly it fails validation for any DateTime or DateTime? that includes a time portion. It only allows through DateTimes where the clock strikes midnight. It's optimised for Cinderella.
  2. It fails validation if the attribute is applied to any property which is not a DateTime or DateTime?.

You can decorate DateTime or DateTime? properties on your model with this attribute like so:


namespace My.Models
{
    public class ImAModelYouKnowWhatIMean
    {
        public int Id { get; set; }

        [DateOnlyAttribute]
        public DateTime ValuationDate { get; set; }

        // Other properties...
    }
}

And if you're using MVC (or anything that makes use of the validation data annotations) then you'll now find that you are nicely protected from DateTimes masquerading as dates. Should they show up you'll find that ModelState.IsValid is false and you can kick them to the curb with alacrity!

Sunday 1 June 2014

Migrating from AngularJS to AngularTS - a walkthrough

It started with nuns. Don't all good stories start that way? One of my (many) aunts is a Poor Clare nun. At some point in the distant past I was cajoled into putting together a simple website for her convent. This post is a walkthrough of how to migrate from AngularJS using JavaScript to AngularJS using TypeScript. It just so happens that the AngularJS app in question is the one that belongs to my mother's sister's convent.

TL;DR - grab what you need

For reference the complete "before" and "after" projects can be found on GitHub here. This is available so people can see clearly what changes have been made in the migration.

The content of the site is available for reference only. (Not that I can really imagine people creating their own "Poor Clares" site and hawking it to convents around the globe but I thought I'd make the point.) It looks like this:

Background

I've been quietly maintaining this website / app for quite a while now. It's a very simple site; 95% of it is static content about the convent. The one piece of actual functionality is a page which allows the user of the website to send a prayer request to the nuns at the convent:

Behind the scenes this sends 2 emails:

  • The first back to the person who submitted the prayer request assuring them that they will be prayed for.
  • The second to the convent telling them the details of what the person would like prayer for.

Right now you are probably thinking this is an unusual post. Perhaps it is, but bear with me.

Over time the website has had many incarnations. It's been table-based layout, it's used Kendo UI, it's used Bootstrap. It's been static HTML, it's been ASP.Net WebForms, it's been ASP.Net MVC and it's currently built using AngularJS with MVC on the back-end to handle bundling / minification and dispatching of emails.

I decided to migrate this AngularJS app to use TypeScript. As I did that I thought I'd document the process for anyone else who might be considering doing something similar. As it happens this is a particularly good candidate for migration as there's a full unit test suite for the app (written with Jasmine). Once I've finished the migration these unit tests should pass, just as they do currently.

You are probably thinking to yourself "but TypeScript is just about adding compile-time annotations right? How could the unit tests not pass after migration?" Fair point, well made. Well that is generally true but I have something slightly different planned when we get to the controllers - you'll see what I mean...

It's also a good candidate for documenting a walkthrough as it's a particularly small and simple Angular app. It consists of just 3 controllers, 2 services and 1 app.

Before I kick off I thought I'd list a couple of guidelines / caveats on this post:

  • I don't intend to say much about the architecture of this application - I want to focus on the migration from JavaScript to TypeScript.
  • The choices that I make for the migration path do not necessarily reflect the "one true way". Rather, they are pragmatic choices that I am making - there may be alternatives approaches here and there that could be used instead.
  • I love Visual Studio - it's my IDE of choice and the one I am using as I perform the migration. Some of the points that I will make are Visual Studio specific - I will try and highlight that when appropriate.

Typings

The first thing we're going to need to get going are the Angular typing files which can be found on Definitely Typed here. Since these typings are made available over NuGet I'm going to pull them in with a wave of my magic Install-Package angularjs.TypeScript.DefinitelyTyped.

As well as pulling in the typing files Visual Studio 2013 has also made some tweaks to my PoorClaresAngular.csproj file which it tells me about:

And these are the TypeScript specific additions that Visual Studio has made to PoorClaresAngular.csproj:


  <Import 
   Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props" 
   Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props')" />

  <TypeScriptToolsVersion>1.0</TypeScriptToolsVersion>

  <Import 
   Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" 
   Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets')" />

I'm going to add one extra of my own:


  <TypeScriptNoImplicitAny>True</TypeScriptNoImplicitAny>

This prevents you having variables of type any in your TypeScript codebase without you implicitly specifying the type. You can live without this but I've found it's useful to catch where you're missing out on the benefit of static typing. Further to that, this option can be particularly useful when performing a migration. It will become obvious why this is the case as we go on.

I decline the kind opportunity to further search NuGet as I'm already on my way typing-wise. So let's review what has happened. Below you can see the typing files that have been pulled in and that the project and packages files were amended:

Changing JS files to TS files

This really should be as simple as changing all the JavaScript files underneath the js directory to have the suffix ts. So going from this:

To this:

And if you're not using Visual Studio it is. But if you are using Visual Studio there's a certain amount of fiddling required to include the generated .js and .js.map files associated with each .ts file. The easiest (hah!) thing to do is to crack open the project and wherever you find a <TypeScriptCompile Include="js\somePath.ts" /> to add in 2 Content statements, one for each generated file which states the dependency on the TypeScript file. For example:


    <TypeScriptCompile Include="js\services\siteSectionService.ts" />
    <Content Include="js\services\siteSectionService.js">
      <DependentUpon>siteSectionService.ts</DependentUpon>
    </Content>
    <Content Include="js\services\siteSectionService.js.map">
      <DependentUpon>siteSectionService.ts</DependentUpon>
    </Content>

It's a bit of a pain to have to do this at the moment. Hopefully the Visual Studio tooling will catch up so this sort of tweaking becomes unnecessary.

Recap

So, where are we? Well, we've got our project ready for TypeScript, we've pulled in the Angular typings from Definitely Typed and we've turned all our JavaScript files in the js directory into TypeScript files.

Now we can actually start working through our TypeScript files and ensuring we're all typed correctly. Please note that because I'm working in Visual Studio I get the benefit of implicit referencing; I don't have to explicitly state the typing files each TypeScript file relies on at the head of the file (eg /// <reference path="angularjs/angular.d.ts" />). If you aren't working in Visual Studio then you'd need to add these yourself.

TypeScriptify app.ts

Opening up app.ts we're presented with a few red squigglies:

These red squigglies are the direct result of my earlier opting in to NoImplicitAny. So in my view it's already paid for itself as it's telling me where I could start using typings. So to get things working nicely I'll give $routeProvider the type of ng.route.IRouteProvider and I'll explicitly specify the type of any for the 2 params parameters:


    // ...
    function ($routeProvider: ng.route.IRouteProvider) {

        function getTheConventTemplateUrl(params: any) {
            var view = params.view || "home";
            return "partials/theConvent/" + view + ".html";
        }

        function getMainTemplateUrl(params: any) {
            var view = params.view || "home";
            return "partials/main/" + view + ".html";
        }

        // ...
    }
    // ...

TypeScriptify siteSectionService.ts

Opening up siteSectionService.ts we're only presented with a single squiggly, and for the same reason as last time:

This error is easily remedied by giving path the type of string.

What's more interesting / challenging is thinking about how we want to enforce the definition of siteSectionService. Remember, this is a service and as such it will be re-used elsewhere in the application (in both navController and mainController). What we need is an interface that describes what our (revealing module pattern) service exposes:


"use strict";

interface ISiteSectionService {
    getSiteSection: () => string;
    determineSiteSection: (path: string) => void;
}

angular.module("poorClaresApp.services").factory(

    "siteSectionService",

    [ // No dependencies at present
    function (): ISiteSectionService {

        var siteSection = "home";

        function getSiteSection() {
            return siteSection;
        }

        function determineSiteSection(path: string) {
            var newSiteSection = "home";

            if (path.indexOf("/theConvent/") !== -1) {
                newSiteSection = "theConvent";
            }
            else if (path !== "/") {
                newSiteSection = "main";
            }

            siteSection = newSiteSection;
        }

        return {
            getSiteSection: getSiteSection,
            determineSiteSection: determineSiteSection
        };
    }]);

As you can see the ISiteSectionService interface is marked as the return type of the function. This ensures that what we return from the function satisfies that definition. Also, it allows us to re-use that interface elsewhere (as we will do later).

TypeScriptify prayerRequestService.ts

Opening up prayerRequestService.ts we're again in NoImplicitAny country:

This is fixed up by defining $http as ng.IHttpService and email and prayFor as string.

As with siteSectionService we need to create an interface to define what prayerRequestService exposes. This leaves us with this:


"use strict";

interface IPrayerRequestService {
    sendPrayerRequest: (email: string, prayFor: string) => ng.IPromise<{
        success: boolean;
        text: string;
    }>;
}

angular.module("poorClaresApp.services").factory(

    "prayerRequestService",

    ["$http",
    function ($http: ng.IHttpService): IPrayerRequestService {

        var url = "/PrayerRequest";

        function sendPrayerRequest(email: string, prayFor: string) {

            var params = { email: email, prayFor: prayFor };

            return $http.post(url, params)
                .then(function (response) {
                    return {
                        success: response.data.success,
                        text: response.data.text
                    };
                });
        }

        return {
            sendPrayerRequest: sendPrayerRequest
        };
    }]);

TypeScriptify prayerRequestController.ts

Opening up prayerRequestController.ts leads me to the conclusion that I have no interesting way left of telling you that we once more need to supply types for our parameters. Let's take it as read that the same will happen on all remaining files as well eh? Hopefully by now it's fairly clear that this option is useful, even if only for a migration. I say this because using it forces you to think about what typings should be applied to your code:

We'll define $scope as ng.IScope, prayerRequestService as IPrayerRequestService (which we created just now) and prayerRequest as { email: string; prayFor: string }. Which leaves me with this:


"use strict";

angular.module("poorClaresApp.controllers").controller(

    "PrayerRequestController",

    ["$scope", "prayerRequestService",
    function ($scope: ng.IScope, prayerRequestService: IPrayerRequestService) {
        
        var vm = this;

        vm.send = function (prayerRequest: { email: string; prayFor: string }) {

            vm.message = {
                success: true,
                text: "Sending..."
            };

            prayerRequestService.sendPrayerRequest(prayerRequest.email, prayerRequest.prayFor)
                .then(function (response) {
                    vm.message = {
                        success: response.success,
                        text: response.text
                    };
                })
                .then(null, function (error) { // IE 8 friendly alias for catch
                    vm.message = {
                        success: false,
                        text: "Sorry your email was not sent"
                    };
                });
        }
    }]);

I could move on but let's go for bonus points (and now you'll see why the unit test suite is so handy). To quote the Angular documentation:

In Angular, a Controller is a JavaScript constructor function that is used to augment the Angular Scope.

So let's see if we can swap over our vanilla contructor function for a TypeScript class. This will (in my view) better express the intention of the code. To do this I am essentially following the example laid down by my Definitely Typed colleague Basarat. I highly recommend his screencast on the topic. Also kudos to Andrew Davey whose post on the topic also fed into this.


"use strict";

module poorClaresApp.controllers {
    class PrayerRequestController {

        static $inject = ["$scope", "prayerRequestService"];
        constructor(
            private $scope: ng.IScope,
            private prayerRequestService: IPrayerRequestService) {
        }

        message: { success: boolean; text: string };

        send(prayerRequest: { email: string; prayFor: string }) {

            this.message = {
                success: true,
                text: "Sending..."
            };

            this.prayerRequestService.sendPrayerRequest(prayerRequest.email, prayerRequest.prayFor)
                .then((response) => {
                    this.message = {
                        success: response.success,
                        text: response.text
                    };
                })
                .then(null, (error) => { // IE 8 friendly alias for catch
                    this.message = {
                        success: false,
                        text: "Sorry your email was not sent"
                    };
                });
        }
    }

    angular.module("poorClaresApp.controllers")
           .controller("PrayerRequestController", PrayerRequestController);
}

My only reservation with this approach is that we have to declare the TypeScript class outside the angular.module... statement. To avoid cluttering up global scope I've placed our class in a module called poorClaresApp.controllers which maps nicely to our Angular module name. It would be nice if I could place the class definition in an IIFE to completely keep this completely isolated but TypeScript doesn't allow for that syntax (for reasons I'm unclear about - the output would be legal JavaScript).

For a small class this seems to add a little noise but as classes grow in complexity I think this approach will quickly start to pay dividends. There are a few things worth noting about the above approach:

  • The required injectable parameters have moved into the class definition in the form of the static $inject statement. I personally like that this no longer sits outside the code it relates to.
  • Because we're using TypeScript arrow functions (which preserve the outer "this" context) we are now free to dispose of the var vm = this; mechanism we're were previously using for the same purpose. Much more intuitive code to my mind.
  • We are not actually using $scope at all in this controller - maybe it should be removed entirely in the long run.

TypeScriptify navController.ts

navController can be simply converted like so:


"use strict";

interface INavControllerScope extends ng.IScope {
    isCollapsed: boolean;
    siteSection: string;
}

angular.module("poorClaresApp.controllers").controller(

    "NavController",

    ["$scope", "siteSectionService",
    function ($scope: INavControllerScope, siteSectionService: ISiteSectionService) {

        $scope.isCollapsed = true;
        $scope.siteSection = siteSectionService.getSiteSection();

        $scope.$watch(siteSectionService.getSiteSection, function (newValue, oldValue) {
            $scope.siteSection = newValue;
        });
    }]);

I'd draw your attention to the creation of a the INavControllerScope interface that extends the default Angular $scope of ng.IScope with 2 extra properties.

Let's also switch this over to the class based approach (there is less of a reason to on this occasion just looking at the size of the codebase but I'm all about consistency of approach):


"use strict";

module poorClaresApp.controllers {
    interface INavControllerScope extends ng.IScope {
        isCollapsed: boolean;
        siteSection: string;
    }

    class NavController {

        static $inject = ["$scope", "siteSectionService"];
        constructor(
            private $scope: INavControllerScope,
            private siteSectionService: ISiteSectionService) {

            $scope.isCollapsed = true;
            $scope.siteSection = siteSectionService.getSiteSection();

            $scope.$watch(siteSectionService.getSiteSection, function (newValue, oldValue) {
                $scope.siteSection = newValue;
            });
        }
    }

    angular.module("poorClaresApp.controllers").controller("NavController", NavController);
}

TypeScriptify mainController.ts

Finally, mainController can be converted as follows:


"use strict";

angular.module("poorClaresApp.controllers").controller(

    "MainController",

    ["$location", "siteSectionService",
    function ($location: ng.ILocationService, siteSectionService: ISiteSectionService) {

        siteSectionService.determineSiteSection($location.path());
    }]);

Again it's just a case of assigning the undeclared types. For completeness lets also switch this over to the class based approach:


"use strict";

module poorClaresApp.controllers {
    class MainController {

        static $inject = ["$location", "siteSectionService"];
        constructor(
            private $location: ng.ILocationService,
            private siteSectionService: ISiteSectionService) {

            siteSectionService.determineSiteSection($location.path());
        }
    }

    angular.module("poorClaresApp.controllers").controller("MainController", MainController);
}

Did it work? Drum roll...

In unit tests we trust. Let's run them...

Success! I hope you found this useful.