Wednesday, September 16, 2015

Using modules in Angular way

You can use a TypeScript module as it is in an Angular app. Just load and it use it right away.

However, if you want it done in Angular way, it's better. It makes the code more testable and the module dependencies on your code will be more obvious.

Here's an example. /shared/utilities/StringLib.ts
///<reference path="../../typings/node/node.d.ts"/>
///<reference path="../../typings/angularjs/angular.d.ts"/>

var isNode = typeof exports !== 'undefined' && this.exports !== exports;

module utilities.StringLib {

    export function isNumeric(n: string) : boolean {
        return !isNaN(parseFloat(n)) && isFinite(<any>n);
    }

    export function replaceAll(src: string, search: string, replacement: string) : string {
        return src.split(search).join(replacement)
    }
}

if (isNode) {
    module.exports = utilities.StringLib;
}
else {
    angular.module('utilities.stringLib',[])
        .factory('stringLib', () => {
            return utilities.StringLib;
        });
}


Then on the ocLazyLoadProvider's config add this:
{
    name: 'utilities.stringLib.files',
    files: ['/shared/utilities/StringLib.js']
}



To use, indicate the module name above on the module's dependency array parameter. On the last element of the array, nest an array, put the name of the list of file(s) of the module(s) that are being lazy-loaded. ocLazyLoad makes this lazy-loading possible.


The typeof operator for utilities.StringLib enables the autocomplete functionality for TypeScript on your IDE.


Note, put the <any> typecast to make the TypeScript compiler stop complaining of incompatible parameter, this lazy-loading mechanism is an extension of ocLazyLoad to Angular, and is not included on Angular's TypeScript definition file.


///<reference path="../../../shared/utilities/StringLib.ts"/>
///<reference path="../../../typings/angularjs/angular.d.ts"/>

module App.Product {

    export class Controller {
    
        constructor(public stringLib: typeof utilities.StringLib) {
        
            console.log(this.stringLib.isNumeric("1234"));
            console.log(this.stringLib.isNumeric("1234b"));        
        }
    
    }

}    
    

angular
    .module('niceApp', <any>
        [
            'utilities.stringLib',

            [                
                'utilities.stringLib.files'                
            ]
        ])
    .controller('ProductController', [
        'stringLib',
        
        App.Product.Controller
    ]);


Tuesday, September 15, 2015

Cannot read property 'name' of undefined

While playing with ocLazyLoad, I got the following error:

TypeError: Cannot read property 'name' of undefined
    at ocLazyLoad.js:554
    at Object.forEach (angular.js:336)
    at Object._loadDependencies (ocLazyLoad.js:539)
    at loadNext (ocLazyLoad.js:642)



Can you spot the error?

// directives
'ngTagsInput',
'ui.pagedown',
'puElasticInput',
'ui.bootstrap'

[
    'sharedState.appWide.files',
    'oitozero.ngSweetAlert.files',


Yeah, I just forgot the comma between 'ui.bootstrap' and the opening square bracket. Should be a syntax error, but due to javascript dynamic nature, it's not flagged as syntax error. Should explore JSLint.

Lazy-loading services with ocLazyLoad

While I'm converting a couchPotato-using Angular code to ocLazyLoad, I learned that ocLazyLoad can lazy load a service too.

On the controller, $ocLazyLoad and $injector must be passed, then get the lazy-loaded object via its name.

Root-Controller.ts:
module App.Root {

    export class Controller {

     
        constructor(
              public SweetAlert,
              public $state,
              
              public $ocLazyLoad,
              public $injector
         ) {

            this.$ocLazyLoad.load('/shared-state/AppWide.js').then(x => {
                var svc = this.$injector.get('someServiceNameHere');
                this.SweetAlert.swal(svc.title);
            });
        }

     

    }

}


angular
    .module('niceApp',<any> 
        [
            'oitozero.ngSweetAlert',   
                      
            [
                'oitozero.ngSweetAlert.files'
            ]
        ])
    .controller('RootController',
        [
            'SweetAlert',
            '$state',
            
            '$ocLazyLoad', 
            '$injector',
            
            App.Root.Controller
        ]);

AppWide.ts:
module SharedState
{
    export class AppWide
    {
        title : string;

        get isUploadVisible(): boolean {
            var theToken = this.$window.localStorage["theToken"];
            return theToken !== undefined;
        }

        constructor(public $window: ng.IWindowService) {
            this.title = "This must be changed";
        }


    }
}

angular.module('niceApp',[])
    .service('someServiceNameHere', 
             ['$window', SharedState.AppWide]);



The problem with the codes above, the controller has now a hard-dependency on $ocLazyLoad and $injector.

There's a nicer solution to the problem, just like SweetAlert, just wrap AppWide.ts to its own module, and make that new module a dependency module of niceApp module. Here's the configuration for AppWide.js:
$ocLazyLoadProvider.config({
            debug: true,
            modules: [
                {
                    name: 'oitozero.ngSweetAlert.files',
                    files: [
                        '/lib/angular-sweetalert/SweetAlert.js',
                        '/lib/sweetalert/dist/sweetalert.min.js',
                        '/lib/sweetAlert/dist/sweetalert.css'
                    ]
                },
                {
                    name: 'common.appWide.files',
                    files: [
                        '/shared-state/AppWide.js'
                    ]
                }
            ]
    });

AppWide.ts:
module SharedState
{
    export class AppWide
    {
        title : string;

        get isUploadVisible(): boolean {
            var theToken = this.$window.localStorage["theToken"];
            return theToken !== undefined;
        }

        constructor(public $window: ng.IWindowService) {
            this.title = "This must be changed";
        }


    }
}

angular.module('common.appWide',[])
    .service('someServiceNameHere', 
             ['$window', SharedState.AppWide]);


The controller is now free from $ocLazyLoad and $injector.

Root-Controller.ts:
module App.Root {

    export class Controller {

    
        constructor(
            public SweetAlert,
            public $state,
            
            public AppWide
        ) 
        {
            this.SweetAlert.swal(this.AppWide.title);
        }

    }

}


angular
    .module('niceApp',<any>
        [
            'oitozero.ngSweetAlert', 
            'common.appWide',
        
            [
                'oitozero.ngSweetAlert.files', 
                'common.appWide.files'
            ]
        ])
    .controller('RootController',
        [
            'SweetAlert',
            '$state',

            'someServiceNameHere', 

            App.Root.Controller
        ]);



Happy Lazy Loading!

Sunday, September 13, 2015

Boilerplate code for AngularJS, NodeJS, JWT and TypeScript

Pre-requisite:

JSON Web Token module:
npm install jwt-simple


Not pre-requisites. Makes UI development convenient.
* ui-router
* sweetAlert


For persistence of token even when the browser is refreshed, $window.sessionStorage would suffice, no need to use ng-storage. However, it's better to use $window.localStorage, $window.sessionStorage is for single tab only; if we open the same app in another tab, it won't be able to read the sessionStorage from the original tab. $window.localStorage can be accessed between tabs.


Authentication consist mainly of these parts:


Server-side:
* Auth.ts -- username and password authentication goes here
* CanBe.ts -- authorization goes here
* app.ts -- entry point of a node app

This is how an API can be authorized in nodejs's app.js. Insert the CanBe.accessedBy node RequestHandler before the API to be called, e.g.,

Without authorization:
app.get('/api/item', itemApi.search);


With authorization:
app.get('/api/item-secured', canBe.accessedBy(['rockstar']), itemApi.search);


Shared by server-side and client-side:
* ILoginDto.ts -- username and password transmitted from client-side to server-side
* ILoginResponseDto.ts -- Auth.ts's authentication response
* ITokenPayload.ts -- Not exactly used by client-side, if there's a need for the client-side to decode the middle part of the JWT, it goes in this data structure. The first part and middle part of JWT are publicly-available information, and as such, ITokenPayload should contain no sensitive information, e.g., password


Client-side:
* init.ts -- called by main.js. main.js is the conventional name for the app's entry point in a RequireJS-based apps.
* AuthService.ts -- called by login UI, to decouple the login controller if there will be changes in authentication mechanism
* HttpInterceptor.ts -- the money shot. this is where to make an angular application automatically send a header everytime an $http or $resource call happens



Auth.ts:
import express = require('express');

import jwt = require('jwt-simple');

export function verify(req: express.Request, res:express.Response, next: Function) : any {
    
    var loginDto = <shared.ILoginDto> req.body;
    
    var loginResponseDto = <shared.ILoginResponseDto> {};
    
    var isValidUser = dbAuthenticate(loginDto.username, loginDto.password);
    
    if (isValidUser) {
        
        var roles = dbGetUserRoles(loginDto.username);
        
        var tokenPayload = <shared.ITokenPayload> { username: loginDto.username, roles: roles };
    
        var payloadWithSignature = jwt.encode(tokenPayload, "safeguardThisSuperSecretKey");
    
        loginResponseDto.isUserValid = true;
        loginResponseDto.theAuthorizationToUse = "Bearer " + payloadWithSignature;
        
        res.json(loginResponseDto);            
    }
    else {
        loginResponseDto.isUserValid = false;
    
        res.status(401).json(loginResponseDto);    
    }    
}


function dbAuthenticate(username: string, password: string): any {
    
    if (username == "Thom" && password == "Yorke") {
        return true;
    }
    
    if (username == "Kurt" && password == "Cobain") {
        return true;
    }
    
    return false;
}


function dbGetUserRoles(username: string): string[] {
    
    if (username == "Thom") {
        return ["rockstar", "composer"];
    }
    
    if (username == "Kurt") {
        return ["composer"];
    }
    
    return [];
}


CanBe.ts:
import express = require('express');

import jwt = require('jwt-simple');


export function accessedBy(roles: string[]):any {
    accessedByBind.bind(roles);
}

function accessedByBind(roles:string[], req:express.Request, res:express.Response, next:Function): any {
    
    function unauthorized(): any {
        return res.sendStatus(401);
    }
    
    // === accessedByBind starts here ===
    
    var theAuthorizationToUse = req.headers["authorization"];
    
    if (theAuthorizationToUse === undefined) {
        return unauthorized();
    }
    
    var payloadWithSignature = theAuthorizationToUse.substr("Bearer ".length); 
    
    var tokenPayload = <shared.ITokenPayload> jwt.decode(payloadWithSignature, "safeguardThisSuperSecretKey");

    if (tokenPayload == undefined || tokenPayload.username == "") {
        return unauthorized();
    }
    
    if (tokenPayload.roles.filter(memberRole => roles.filter(allowedRole => memberRole == allowedRole).length > 0 ).length > 0) {
        return next();
    }
    
    return res.status(500).json({customError: "Unknown Error"});
    
}



ILoginDto.ts:
module shared {
    
    export interface ILoginDto {
        username: string;
        password: string;
    }
    
}

ILoginResponseDto.ts:
module shared {

    export interface ILoginResponseDto {
        isUserValid: boolean;
        theAuthorizationToUse: string;
    }
    
}

ITokenPayload.ts:
module shared {
    
    export interface ITokenPayload {
    
        username: string;
        roles: string[];
    
    }
}



init.js
// app initialization and dependency injections goes here

var angie:ng.IAngularStatic = require('angular');

var mod:ng.IModule = angie.module('niceApp', [
    'ui.router', 'ngResource', 'scs.couch-potato', 'ngFileUpload',
    'ngSanitize', 'ui.pagedown', 'ngTagsInput', 'puElasticInput', 'ui.bootstrap']);

mod.service('AuthService', ['$http', '$q', '$window', InhouseLib.AuthService]);
mod.factory('HttpInterceptor', ['$q', '$window', '$injector', 'TheSweetAlert', '$location', InhouseLib.HttpInterceptor]);

mod.config(['$httpProvider', ($httpProvider:ng.IHttpProvider) => {
    $httpProvider.interceptors.push('HttpInterceptor');
}]);


AuthService.ts:
module InhouseLib {

    export class AuthService {


        constructor(public $http:ng.IHttpService, public $q:ng.IQService, public $window: ng.IWindowService) {
        }

        verify(username:string, password:string):ng.IPromise<ng.IHttpPromiseCallbackArg<shared.ILoginResponseDto>> {

            var deferred = this.$q.defer();

            var u = <shared.ILoginDto>{};
            u.username = username;
            u.password = password;



            this.$http.post<shared.ILoginResponseDto>('/api/member/verify', u)
                .then(success => {

                    this.$window.localStorage["theToken"] = success.data.theAuthorizationToUse;

                    // This goes to then's success callback parameter
                    deferred.resolve(success);

                }, error => {

                    this.logout();

                    // this goes to then's error callback parameter
                    deferred.reject(error);

                });

            return deferred.promise;

        }

        logout(): void {
            delete this.$window.localStorage["theToken"];
        }

    }
}


HttpInterceptor.ts:
module InhouseLib {

    export function HttpInterceptor($q:ng.IQService,
                                    $window:ng.IWindowService,
                                    $injector:ng.auto.IInjectorService,
                                    TheSweetAlert,
                                    $location:ng.ILocationService)
    {

        return {
            request: (config):any => {
                config.headers = config.headers || {};

                var theToken = $window.localStorage["theToken"];

                if (theToken !== undefined) {
                    // token already include the word "Bearer "
                    config.headers.Authorization = theToken;
                }
                return config;
            },

            responseError: (response):any => {
                if (response.status === 401 || response.status === 403) {

                    // http://stackoverflow.com/questions/25495942/circular-dependency-found-http-templatefactory-view-state
                  
                    // ui-router's $state
                    // can use the $location.path(paramHere) to change the url. but since we are using ui-router
                    // it's better to use its $state component to route things around.
                    var $state:any = $injector.get('$state');

                    TheSweetAlert({title: "Oops", text: "Not allowed. Will redirect you to login page\n" +
                        "This is your current url: " + $location.path()}, () =>
                    {
                        $state.go('root.login');
                    });


                }
                return $q.reject(response);
            }
        };
    }// function HttpInterceptor

}

UPDATE:

Some code above has Cargo Culting in it, it uses $q.defer unnecessarily. Haven't properly learned the promise's fundamentals before :) Read: https://www.codelord.net/2015/09/24/%24q-dot-defer-youre-doing-it-wrong/

Thursday, September 10, 2015

Avoid multiple NOTs in conditions

TL;DR
Write code like how you are speaking in a conversation.


What's the flaw in this code?

if (e.which === 8 && e.target.nodeName !== "INPUT" || e.target.nodeName !== ")
    e.preventDefault();
}


How to express condition without being confused on when to use || or && operator?


First off, let's correct the most fundamental error on the logic above. && operator has higher precedence than || operator, or we can say && operator higher stickiness than || operator. The above is interpreted as:

if 
(

    (e.which === 8 && e.target.nodeName !== "INPUT")
 
    || 

    e.target.nodeName !== "SELECT"

)
{ 
    e.preventDefault();
}


To correct the incorrect unparenthesized code, use parenthesis:

if ( e.which === 8 && (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") ) { 
    e.preventDefault();
}


Or since the code is not too indented, just nest the condition:

if (e.which === 8) {

    if (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") { 
         e.preventDefault();
    }

}


Now that we get the harm out of the way, let's fix the second problem:

if (e.target.nodeName !== "INPUT" || e.target.nodeName !== "SELECT") {

}


What's wrong with the code above?

Yes, it always evaluates to true regardless of the value of e.target.nodeName.

This is where most developers are having a hard time when formulating a condition. The best way to express a logic is to express it like how you will say it in actual conversation. For example, if you want to advise your kid that if someone visits and he is not John, Paul, George, Ringo, then shoo him way. You will not say in English, "if he is not John, not Paul, not George, not Ringo, then shoo him away."

You will not repeat the NOT when you actually talk, instead you'll say: "if he is not John, Paul, George, Ringo, then shoo them away." You will only say NOT once. However, if you feel multiple NOTs is readable, you have to change the OR to AND to make the logic correct:

if e.target.nodeName ≠ "INPUT" and e.target.nodeName ≠ "SELECT" then
    disable navigation

But really, you won't say that in real life, you'll only say NOT once.

To code a correct logic, think of how you will write the condition if you will refactor or make a code shortcut. If you will move the condition to a function, you'll write it like this:

if not inputEditable(e.target.nodeName) then
    disable navigation


Now with that refactored code, the code writes itself:

inputEditable(nodeName) 

    if nodeName = "INPUT" or nodeName = "SELECT" then
        return true
    else
        return false

Here's the version when the condition above is inlined in if:

if not ( e.targetNodeName = "INPUT" or e.target.nodeName = "SELECT" ) then
    disable navigation


See? No more multiple NOTs, and the best thing is, the code is correct.


Just write things with one NOT, the right logic will write itself.



Happy Coding!

Tuesday, September 8, 2015

Using hallo on AngularJS and RequireJS (troubleshooting)

Requirement:
1. Hallo
npm install hallo

2. Hallo Angular Directive
Get the angular directive here: http://www.grobmeier.de/using-hallo-js-with-angularjs-14072013.html

3. Hallo requirejs.config paths:
"jQuery": "/lib/hallo/deps/jquery-1.9.0",
"jQueryUI" : "/lib/hallo/deps/jquery-ui-1.10.0.custom",
"rangyCore" : "/lib/hallo/deps/rangy-core-1.2.3",
"halloJS" : "/lib/hallo/examples/hallo",

4. require's prior to Angular app initialization:
define(require => {

    require('jQuery');
    require('jQueryUI');
    require('rangyCore');
    require('halloJS');

    var angie: ng.IAngularStatic = require('angular');
    createHalloDirective(angie); // http://www.grobmeier.de/using-hallo-js-with-angularjs-14072013.html">http://www.grobmeier.de/using-hallo-js-with-angularjs-14072013.html

    var mod: ng.IModule = angie.module('niceApp', []);



/lib points to node_modules directory.



If you encountered this error:
TypeError: element.hallo is not a function

You need to use full jQuery instead of Angular's jQLite.


In your requirejs.config shim section, you must specify jQuery as a dependency of Angular, if it's not specified it will load jQLite instead:

"angular" : {
    "exports": "angular",
    deps: ["jQuery"]
}


You'll encounter these errors if you don't define the depedencies between hallo, rangy and jQueryUI:
Uncaught TypeError: $.widget is not a function
  (anonymous function) 
  (anonymous function) 
TypeError: element.hallo is not a function


To fix that, must add these dependencies on your requirejs.config shim section:
"jQueryUI": {
    deps: ["jQuery"]
},

"rangyCore" : {
    deps: ["jQueryUI"]
},

"halloJS" : {
    deps: ["rangyCore"]
}



Hallo directive should load correctly on your Angular app. However when you double click the hallo content, it'll show the following errors:
jQuery.fn.jQuery.init[1]
Uncaught TypeError: rangy.getSelection is not a function
  jQuery.widget.getSelection 


The rangy-core that's bundled via hallo obtained from npm yields that error. To fix that, install rangy directly:

npm install rangy

Then use that instead of the rangy-core that's bundled with hallo.

Point rangyCore to new path on requirejs.config paths:
"jQuery": "/lib/hallo/deps/jquery-1.9.0",
"jQueryUI" : "/lib/hallo/deps/jquery-ui-1.10.0.custom",
// "rangyCore" : "/lib/hallo/deps/rangy-core-1.2.3", // remove this
"rangyCore" : "/lib/rangy/lib/rangy-core", // change to this
"halloJS" : "/lib/hallo/examples/hallo",


There's still an error when you double click the hallo content, it'll show the following:
jQuery.fn.jQuery.init[1]
Uncaught ReferenceError: rangy is not defined
  jQuery.widget.getSelection 


To fix that that, put the rangy's instance on window:
require('jQuery');
require('jQueryUI');

var rangy = require('rangyCore');
window["rangy"] = rangy;
    
require('halloJS'); // hallo looks for rangy in global scope, that is, window

That's it, when you double click the hallo's content, the tools for formatting(Bold, Italic, etc) should pop up now.

Monday, September 7, 2015

TypeScript All Things

On following Angular controller code, the second parameter Item looks odd, it has no type.

And the only way we can get TypeScript's autocomplete goodness on that Item service is to explicitly assign the type on Item service's $promise's callback argument. We cannot get autocomplete support on customQuery method.

constructor(
    product: Domain.Product, 
    Item, 
    $stateParams, 
    appWide: SharedState.AppWide) 
{

    appWide.title = "Products";

    this.product = product;

    var tag = $stateParams.tag === undefined ? "" : $stateParams.tag;

    product.searched = tag === "" ? "" : "tag:" + tag.replaceAll("--", " ");

    product.search = () => {
        this.searched = product.searched;

        Item.customQuery({keywords: this.searched})
            .$promise.then((items : 
                Dto.SearchDto
                ) => {
                this.itemSearched = items.matches;
                product.tags = items.tags;
                product.locations = items.locations;
            });
    }; // search

    product.search();

}

Definition of Dto.SearchDto:
module Dto {
    export class SearchLocationDto {
        locationId: string;
        locationName: string;
    }

    export class SearchItemDto {
        itemId : number;
        itemTitle: string;
        itemDescription: string;
        itemTags: string;
    }

    export class SearchDto {
        matches: SearchItemDto[];
        tags: string[]  ;
        locations: SearchLocationDto[];
    }
}

Definition of Dto.IItemReturnedDto. This is the type of save's then's success callback argument. Just a primary key
module Dto {
    export interface IItemReturnedDto {
        item_id : number;
    }
}


Then on module initialization, objects are just returned typeless:
var mod:ng.IModule = angie.module('niceApp', ['ui.router', 'ngResource', 'scs.couch-potato', 'ngFileUpload']);

mod.factory('Item', ['$resource', ($resource:angular.resource.IResourceService) => {

    return $resource("/api/item-secured/:theItemId", null, {
        customQuery: {method: 'GET', isArray: false}
    });

}]);


To improve the above, provide type on a service derived from Angular's $resource. Following is the interface definition of customized angular resource method customQuery.
module InhouseLib {

    export interface ISearchParameter {
        keywords: string;
    }

    // the returned when doing save on resource is Dto.IItemReturnedDto
    export interface ISearchResource extends ng.resource.IResourceClass<ng.resource.IResource<Dto.IItemReturnedDto>> { 
        customQuery(ISearchParameter) : angular.resource.IResource<Dto.SearchDto>;
    }
}

Then on module initialization, use the type defined above.
mod.factory('Item', ['$resource', ($resource:ng.resource.IResourceService) : InhouseLib.ISearchResource => {


   var customQueryAction : ng.resource.IActionDescriptor = {
       method: 'GET',
       isArray: false
   };

   var r = <InhouseLib.ISearchResource> $resource ("/api/item/:theItemId", null, {
       customQuery: customQueryAction
   });

   return r;

}]);


Now on controller constructor, add the type for Item (line 3 on code at the bottom). Doing that, the autocomplete for customQuery on Item service shall pop-up upon typing the dot character.




And also, we can also now eliminate (on line 20) the type (Dto.SearchDto) of the callback argument items:

constructor(
     product: Domain.Product, 
     Item: InhouseLib.ISearchResource, 
     $stateParams, 
     appWide: SharedState.AppWide) 
 {

    appWide.title = "Products";

    this.product = product;

    var tag = $stateParams.tag === undefined ? "" : $stateParams.tag;

    product.searched = tag === "" ? "" : "tag:" + tag.replaceAll("--", " ");

    product.search = () => {
        this.searched = product.searched;

        Item.customQuery({keywords: this.searched})
            .$promise.then(items => {
                this.itemSearched = items.matches;
                product.tags = items.tags;
                product.locations = items.locations;
            });

    }; // search

    product.search();

}

And yet the IDE is smart to know the type and it gives an autocomplete when you type the variable name(items) and dot symbol. TypeScript and WebStorm IDE are nice.

Sunday, September 6, 2015

Custom Basic Authentication and Authorization with AngularJS, Node Express, TypeScript, Massive DAL and PostgreSQL

Design:
    • On successful login, the server shall send back the authentication header that will be saved to angular's $http pipeline
    • Authorization is roles-based
    • Member's role(s) are stored as text array in the database

Prerequisites:
    • Data Access Layer node module
        ○ npm install massive
    • Basic Authentication node module
        ○ npm install basic-auth



I'll go owl on this and just do a code dump and briefly explain each part and their relation to other parts.


What will be created:
    
    • Login screen:
        1. /public/app-dir/Login/Template.html
        2. /public/app-dir/Login/Controller.ts
        
    • Login and Response DTOs shared by client-side and server-side
    
        3. /shared/dto/LoginDto.ts
        4. /shared/dto/LoginResponseDto.ts
        
    • Authentication service for Angular
        5. /public/inhouse-lib/AuthService.ts
    
    • Authentication node service
        6. /api/member.ts
        7. /db/loginVerify.sql

    • Authorization for node REST APIs
        8. /server/CanBe.ts
        9. /db/verifyRoleAccess.sql

    • Member table
        10. server/ddl.sql

    
What need to be changed:
        11. Nodejs's app.ts
            i. Add initialization of massive DAL
            ii. Add authentication url to node
            iii. Authorize a REST API




1. public/app-dir/Login/Template.html
<style>

    .block label { display: inline-block; width: 140px; text-align: left; }

</style>

<div class="col-md-2 col-md-offset-5">
    <form ng-submit="c.login()">
        <div class="block">
            <label>Username</label>
            <input type="text" ng-model="c.username"/>
        </div>

        <div class="block">
            <label>Password</label>
            <input type="password" ng-model="c.password"/>
        </div>

        <div class="block">
            <label></label>
            <input type="submit" value="Login"/>
        </div>

    </form>
</div>

2. public/app-dir/Login/Controller.ts
///<reference path="../../lib-inhouse/doDefine.ts"/>
///<reference path="../../../typings/angularjs/angular.d.ts"/>
///<reference path="../../lib-inhouse/AuthService.ts"/>
///<reference path="../../../typings/sweetalert/sweetalert.d.ts"/>
///<reference path="../../shared-state/AppWide.ts"/>

module App.Login {

    export class Controller {


        username:string;
        password:string;


        // Dependency-injected sweet alert, so it can be easily mocked

        constructor(public authService:InhouseLib.AuthService, public $http:ng.IHttpService, public $state:any,
                    public TheSweetAlert:SweetAlert.SweetAlertStatic,
                    public appWide:SharedState.AppWide) {
        }


        login():void {


            this.authService.verify(this.username, this.password)
                .then(success => {
                    this.appWide.isUploadVisible = true;
                    this.$state.go('root.app.home');
                }, error => {
                    this.appWide.isUploadVisible = false;
                    this.TheSweetAlert(error.status.toString(), "Not authorized", "error");
                });


        }

    }
}


doDefine(require => {

    var mod:angular.IModule = require('eat');


    require('/shared/dto/LoginDto.js');


    mod["registerController"]('LoginController', ['AuthService', '$http', '$state', 'TheSweetAlert', 'AppWide',
        App.Login.Controller]);

});


authService.verify line 27, is where the setting and clearing of Basic Authentication information to Angular's $http pipeline happens, authService.verify returns a promise if the authentication is successful, if the user is not authenticated it goes to the promise's error callback parameter.


public/lib-inhouse/doDefine.ts
///<reference path="../../typings/requirejs/require.d.ts"/>

// the doDefine will be disabled during unit test

function doDefine(func: (cb) => any) {

    define(func);

}

3. shared/dto/LoginDto.ts
///<reference path="../../typings/node/node.d.ts"/>


module Dto {

    export class LoginDto {

        username: string;
        password: string;

    }

}



4. shared/dto/LoginResponseDto.ts
///<reference path="../../typings/node/node.d.ts"/>

module Dto {

    export class LoginResponseDto {

        isValidUser: boolean;

        theBasicAuthHeaderToUse: string;

    }

}

The theBasicAuthHeaderToUse is the header that will be received by Angular and shall be assigned to Angular's $http pipeline. Its format is:

Basic base64encodeOf(username + ': ' + password)

Sample format:
Basic VGhvbSBZb3JrZTpjcmVlcA==


5. public/lib-inhouse/AuthService.ts
///<reference path="../../typings/angularjs/angular.d.ts"/>
///<reference path="../../shared/dto/LoginDto.ts"/>
///<reference path="../../shared/dto/LoginResponseDto.ts"/>

module InhouseLib {

    export class AuthService {


        constructor(public $http:ng.IHttpService, public $q:ng.IQService) {
        }

        verify(username:string, password:string):ng.IPromise<ng.IHttpPromiseCallbackArg<Dto.LoginResponseDto>> {

            var deferred = this.$q.defer();

            var u = new Dto.LoginDto();
            u.username = username;
            u.password = password;



            this.$http.post<Dto.LoginResponseDto>('/api/member/verify', u)
                .then(success => {


                    this.$http.defaults.headers["common"]["Authorization"] = success.data.theBasicAuthHeaderToUse;

                    // This goes to then's success callback parameter
                    deferred.resolve(success);

                }, error => {

                    this.logout();

                    // this goes to then's error callback parameter
                    deferred.reject(error);

                });

            return deferred.promise;

        }

        logout(): void {
            delete this.$http.defaults.headers["common"]["Authorization"];
        }

    }

}



6. Authentication module. Do the base64 encoding of Basic username:passwordHere at the server.

api/member.ts
/// <reference path="../typings/express/express.d.ts"/>
/// <reference path="../typings/extend/extend.d.ts"/>
/// <reference path="../shared/dto/LoginDto.ts"/>
///<reference path="../shared/dto/LoginResponseDto.ts"/>


import express = require("express");

import extend = require('extend');


export function verify(req:express.Request, res:express.Response, next:Function):any {


    var app:express.Application = req["app"];
    var db = app.get('db');


    var loginDto = <Dto.LoginDto> req.body;
    console.log(loginDto.username);

    var loginResponseDto = <Dto.LoginResponseDto>{};

    db.loginVerify([loginDto.username, loginDto.password], (err, result) => {


        if (result.length == 0) {

            loginResponseDto.isValidUser = false;
            loginResponseDto.theBasicAuthHeaderToUse = "Ha! Are you expecting a returned value?!";
            res.status(401).json(loginResponseDto);

        }
        else {

            var member = result[0];
            var salted_password = member.salted_password;

            loginResponseDto.isValidUser = true;
            loginResponseDto.theBasicAuthHeaderToUse = 
                "Basic " + new Buffer(loginDto.username + ":" + loginDto.password).toString("base64");
            res.json(loginResponseDto);
        }

    });

}



7. db/loginVerify.sql

This is called by the Authentication module.

For a primer of bcrypt mechanism: http://www.ienablemuch.com/2014/10/bcrypt-primer.html

select member_name, salted_password
from member
where member_name = $1 and salted_password = crypt($2, salted_password);

This is where the authorization happens.


8. Authorization module

server/CanBe.ts
/// <reference path="../typings/express/express.d.ts"/>

import express = require('express');

var basicAuth = require('basic-auth');


export function accessedBy(roles:string[]):express.RequestHandler {
    return accessedByBind.bind(undefined, roles);
}


function accessedByBind(roles:string[], req:express.Request, res:express.Response, next:Function): any {

    function unauthorized(): any {
        res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
        return res.sendStatus(401);
    }


    // === accessedByBind starts here ===

    var app:express.Application = req["app"];
    var db = app.get('db');


    var user = basicAuth(req);

    if (!user || !user.name || !user.pass) {
        return unauthorized();
    }

    var username = user.name;
    var password = user.pass;

    db.verifyRoleAccess([username, password, roles], (err, result) => {

        if (err == null) {
            var isAllowed = result[0].is_allowed;

            if (isAllowed)
                return next();
            else {
                return unauthorized();
            }
        }
        else {
            return res.status(500).json({customError: 'Unknown Error'});
        }

    });


}



9. db/verifyRoleAccess.sql

This is called by the CanBe Authorization module.

select exists(

select null
from member
where
member_name = $1 and salted_password = crypt($2, salted_password)
and roles && $3 -- check if $3 (arrayOfRoles) passed from canBe.accessedBy(arrayOfRolesHere) is in member.roles.

) as is_allowed;



10. server/ddl.sql
create table member
(
    member_id serial primary key,
    member_name citext not null unique,
    salted_password text not null, -- using bcrypt
    roles text[]
);

insert into member(member_name, salted_password, roles) values
('Thom Yorke', crypt('creep', gen_salt('bf')), '{"rockstar"}')


11. app.ts
var massive = require('massive'); // DAL
import memberApi = require('./api/member'); // authentication 
import canBe = require('./server/CanBe'); // authorization


var massiveInstance = massive.connectSync({connectionString: 'postgres://postgres:yourPasswordHere@localhost/commerce'});
app.set('db', massiveInstance);

app.post('/api/member/verify', memberApi.verify);

// unsecured API
app.get('/api/item', itemApi.search);

// just insert canBe.accessedBy before the itemApi.search RequestHandler REST API to secure the REST API
app.get('/api/item-secured', canBe.accessedBy(['rockstar']), itemApi.search); 

UPDATE:

Some code above has Cargo Culting in it, it uses $q.defer unnecessarily. Haven't properly learned the promise's fundamentals before :) Read: https://www.codelord.net/2015/09/24/%24q-dot-defer-youre-doing-it-wrong/

Don't marshal when you can cast

var loginDto = <Dto.LoginDto> {};
extend(loginDto, req.body); // marshal the req.body dictionary to strongly-typed object
console.log(loginDto.username);

The code above is inefficient, extending is an expensive process. The above could be simply rewritten as:

var loginDto = <Dto.LoginDto> req.body;
console.log(loginDto.username);


Imagine the nodejs code above if done with statically-typed framework such as ASP.NET MVC, the values from Request.Form (same as nodejs's req.body) need to be marshalled to DTO object every time a request is made, a bit inefficient.


You can make something efficient and be efficient (autocomplete support) when coding with TypeScript.

Thin Angular Controller

An example of caller of Authentication API:

App.Login.Controller.ts:
module App.Login {

    export class Controller {


        username:string;
        password:string;

        // Dependency-injected sweet alert, so it can be easily mocked
        constructor(public authApi:InhouseLib.AuthApi, public $http:ng.IHttpService, public $state:any,
                    public TheSweetAlert:SweetAlert.SweetAlertStatic,
                    public appWide:SharedState.AppWide) {
        }


        login():void {


            this.authApi.verify(this.username, this.password)
                .then(success => {
                    console.log(success.data.isValidUser);
                    this.appWide.isUploadVisible = true;
                    this.$http.defaults.headers["common"]["Authorization"] = success.data.theBasicAuthHeaderToUse;
                    this.$state.go('root.app.home');
                }, error => {
                    console.log(error.data.isValidUser);                    
                    this.appWide.isUploadVisible = false;
                    delete this.$http.defaults.headers["common"]["Authorization"];
                    this.TheSweetAlert("Oops...", "Not authorized", "error");                
                });

            }//login
            
        }//Controller
}


An example of Authentication API, AuthApi.ts:

///<reference path="../../typings/angularjs/angular.d.ts"/>
///<reference path="../../shared/dto/LoginDto.ts"/>
///<reference path="../../shared/dto/LoginResponseDto.ts"/>

module InhouseLib {

    export class AuthApi {

        constructor(public $http : ng.IHttpService) {

        }

        verify(username: string, password: string) : ng.IHttpPromise<Dto.LoginResponseDto> {

            var u = new Dto.LoginDto();
            u.username = username;
            u.password = password;


            return this.$http.post<Dto.LoginResponseDto>('/api/member/verify', u);

        }

    }

}


// Angular initialization code:
// mod.service('AuthApi', ['$http', InhouseLib.AuthApi]);



The problem with the caller(Login Controller) of the Authentication API is it does things that should not be of concern to it. One concern that should not be in the controller is the assigning and invalidating of Basic Authentication to angular's $http pipeline.


To improve that. The assigning and invalidating of Basic Authentication information to angular's $http pipeline should be done on AuthApi.ts. Following is an example. First, we must remove the code related to Basic Authentication away from the concerns of Login controller.

login():void {

    this.authApi.verify(this.username, this.password)
        .then(success => {
             console.log(success.isValidUser);
             this.appWide.isUploadVisible = true;
             this.$state.go('root.app.home');
        }, error => {
             console.log(error.isValidUser);                
             this.appWide.isUploadVisible = false;
             this.TheSweetAlert("Oops...", "Not authorized", "error");
        });


}


Then move that concern to AuthApi.ts:

verify(username: string, password: string) : ng.IPromise<Dto.LoginResponseDto> {

    var deferred = this.$q.defer();

    var u = new Dto.LoginDto();
    u.username = username;
    u.password = password;


    this.$http.post<Dto.LoginResponseDto>('/api/member/verify', u)
    .then(success => {

        this.$http.defaults.headers["common"]["Authorization"] = success.data.theBasicAuthHeaderToUse;

        // This goes to then's success callback parameter
        deferred.resolve(success.data);

    }, error => {

        delete this.$http.defaults.headers["common"]["Authorization"];

        // this goes to then's error callback parameter
        deferred.reject(error.data);

    });

    return deferred.promise;

}


If the caller code (Login Controller) has a need receive http status code returned by the server, return a promise of IHttpPromiseCallbackArg with an argument of the other information(Dto.LoginResponseDto) instead.

verify(username: string, password: string) :     
    ng.IPromise<ng.IHttpPromiseCallbackArg<Dto.LoginResponseDto>> 
{

    var deferred = this.$q.defer();

    var u = new Dto.LoginDto();
    u.username = username;
    u.password = password;


    this.$http.post<Dto.LoginResponseDto>('/api/member/verify', u)
        .then(success => {
            this.$http.defaults.headers["common"]["Authorization"] = success.data.theBasicAuthHeaderToUse;

            // This goes to then's success callback parameter
            deferred.resolve(success);

        }, error => {

            delete this.$http.defaults.headers["common"]["Authorization"];

            // this goes to then's error callback parameter
            deferred.reject(error);

        });

    return deferred.promise;

}


To access the other information(Dto.LoginResponseDto), access it via success.data and error.data. For the http status code, access it from success or error parameter, e.g., error.status.

login():void {


     this.authApi.verify(this.username, this.password)
         .then(success => {
             console.log(success.data.isValidUser);
             this.appWide.isUploadVisible = true;
             this.$state.go('root.app.home');
         }, error => {
             console.log(error.data.isValidUser);                
             this.appWide.isUploadVisible = false;
             this.TheSweetAlert(error.status.toString(), "Not authorized", "error");
         });

}

WebStorm Need Tight TypeScript Integration

Seriously.


Whereas Visual Studio Code coding assistance almost works flawlessly. For example Visual Studio Code shows on the tooltip the type of the parameter when hovering over the parameter:



..WebStorm has problem knowing the type:



Wishing there is TypeScriptStorm.

Thursday, September 3, 2015

Fast text search in PostgreSQL

This works in Postgres:


select * from location where location_id = any(array[1,6,8])

However, if you tried to match it against an array on another query, it won't work, even though the another query returns exactly one row only.

select * from location where location_id = any(select location_ids from province p where p.province_name = 'Metro Manila' limit 1)

Output:
ERROR:  operator does not exist: integer = integer[]
LINE 1: select * from location where location_id = any
                                                 ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.


To correct it, unnest the array:

select * from location where location_id = any(select unnest(location_ids) from province p where p.province_name = 'Metro Manila')

Might as well use IN:

select * from location where location_id in (select unnest(location_ids) from province p where p.province_name = 'Metro Manila')


There's a drawback on the queries above, it uses equal operator. In Postgres, indexers are bound to operator, and GIN, the indexing technology for indexing array, doesn’t support equal operator. See the detail here: http://stackoverflow.com/questions/4058731/can-postgresql-index-array-columns/29245753#29245753


So to optimize the query, use either of the following:


-- This query works same as the query at the bottom. The query at the bottom is more readable though.
-- The advantage of this query is if the province matches many rows, it would still work, say the province_name uses ILIKE operator.

select  *
from    location l
where   exists
        (
            select  p.location_ids
            from    province p
            where   p.province_name = 'Metro Manila'
                    and array[l.location_id] && p.location_ids
        );

The query uses overlap operator, &&. See the use of supported operators on arrays: http://www.postgresql.org/docs/9.1/static/functions-array.html

-- If province returns exactly one row, this is more readable than above
select   *
from     location l
where    array[l.location_id] && ( select location_ids p from province p where p.province_name = 'Metro Manila' );


Live code: http://sqlfiddle.com/#!15/6ac38/5


Happy Coding!