Monday, 12 August 2019

Searching for 'Angularized values' in MS Sql Server DB

The following query can be used to look for 'Angularized values' in MS Sql Server DB.
DECLARE @sql nvarchar(1024)
DECLARE @column_name varchar(200)
DECLARE @table_name varchar(200)
DECLARE @sp_out varchar(2048)
DECLARE @x int

create table #tmp(sp_out varchar(2048), table_name varchar(255), column_name varchar(2048), id uniqueidentifier)
   
DECLARE tn_cursor CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.tables

open tn_cursor 
FETCH NEXT FROM tn_cursor
INTO @table_name

WHILE @@FETCH_STATUS = 0    
BEGIN   
                print @table_name

                DECLARE cn_cursor CURSOR FOR
                SELECT COLUMN_NAME
                FROM INFORMATION_SCHEMA.columns
                WHERE TABLE_NAME = @table_name

                open cn_cursor 
                FETCH NEXT FROM cn_cursor
                INTO @column_name

                WHILE @@FETCH_STATUS = 0    
                BEGIN   
                                FETCH NEXT FROM cn_cursor
                                INTO @column_name

                                --print @column_name

                                set @sql = 
                                N'insert into #tmp SELECT TOP (1000) ' + @column_name + ', ''' + @table_name + ''', ''' + @column_name + ''', Id' +
                                N' FROM [dbo].[' + @table_name + N']' +
                                N' WHERE ' + @column_name + N' like ''%? % ?%'''
                
                                --print @sql
                                exec sp_executesql @sql
                END

                CLOSE cn_cursor;    
                DEALLOCATE cn_cursor; 

                FETCH NEXT FROM tn_cursor
                INTO @table_name
END

CLOSE tn_cursor;    
DEALLOCATE tn_cursor; 

select * from  #tmp
drop table #tmp



This query will look for all fields in the DB with the contents with pattern '%? % %?' that matches Angularized values. An Angularized value is generated by AngularJs when autogenerated values are made when the contents of a drop down does not match the HTML.

Tuesday, 6 August 2019

Font Awesome 5 in Angular 8

Font Awesome 5 in Angular 8 based app! Here is how I did it, I first import the Font awesome packages into the app module:
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
Importing the FontAwesomeModule also into the imports section.

Add the following in to your constructor of app module:
 constructor(){
    library.add(fab, far, fas);
  }
Now you can reference the Font Awesome icons from inside any component like in this markup example:
   <div class="crop"
     (click)="onClick()"
     [style.width.px]="starWidth"
     [title]="rating">
  <div style="width: 75px">
    <span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
    <span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
    <span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
    <span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
    <span><fa-icon [icon]="['far', 'star']"></fa-icon></span>
  </div>
</div>
Note that if you do not use the solid icons from the 'fas' library, you must specify the type of Font Awesome icon library, such as 'far' for the regular icons. I ended up with using the following npm packages: "@fortawesome/angular-fontawesome": "^0.3.0", "@fortawesome/fontawesome-svg-core": "^1.2.21", "@fortawesome/free-brands-svg-icons": "^5.10.1", "@fortawesome/free-regular-svg-icons": "^5.10.1", "@fortawesome/free-solid-svg-icons": "^5.10.1", Note: I did a downgrade to version 0.3.0 of the angular-fontawesome package. Tested out in Angular 8. Note that adding the entire library is not suggested in most cases, as this will increase the bundle size Webpack builds up. Instead, add the necessary icons one by one. However, in a development period, it is neat to have all the (Free) icons from Font Awesome readily available until it is production/deploying time! Happy Angular 8 coding!
Example of a shared module you can set up to import in Angular 8 supporting Font Awesome five is below:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { StarComponent } from "src/app/shared/star.component";
import { FormsModule } from "@angular/forms";
import { fas } from '@fortawesome/free-solid-svg-icons';
import { far } from '@fortawesome/free-regular-svg-icons';
import { fab } from '@fortawesome/free-brands-svg-icons';
import { library } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';

@NgModule({
  declarations: [
    StarComponent
  ],
  imports: [
    CommonModule,
    FontAwesomeModule
  ],
  exports: [
   StarComponent,
   CommonModule,
   FormsModule,
   FontAwesomeModule,
  ]
})
export class SharedModule {

  constructor () {
    library.add(fab, far, fas);
  }

}

Angular - Displaying localized currency - setting up locale to Norwegian (example)

This article will quickly describe how we can set up a locale for Angular; to display for example currency in a correct manner. First off we import the necessary locale setup in our main module, e.g. app.module.ts :
import { NgModule, LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import  localeNor from '@angular/common/locales/nb';
import  localeNorExtra from '@angular/common/locales/nb';
Note here that this setup targets Norwegian locale. You can see a list of these locales in the parent folder of this url: https://github.com/angular/angular/blob/master/packages/common/locales/nb.ts We also import the 'extra' information for locales - the Norwegian locale here. The parent folder show the available locales in this url: https://github.com/angular/angular/blob/master/packages/common/locales/extra/nb.ts Then we set up the Norwegian locale using the method registerLocaleData below the imports of our app module with a call to the method. registerLocaleData(localeNor, 'no', localeNorExtra); We also set up the providers for the app module to use the 'no' variable for LOCALE_ID A complete sample of the app module looks like this then:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, LOCALE_ID } from '@angular/core';
import { registerLocaleData } from '@angular/common';
import  localeNor from '@angular/common/locales/nb';
import  localeNorExtra from '@angular/common/locales/nb';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { ProductListComponent } from "src/app/products/product-list.component";
import { WelcomeComponent } from "src/app/home/welcome.component";

registerLocaleData(localeNor, 'no', localeNorExtra);

@NgModule({
  declarations: [
    AppComponent,
    ProductListComponent,
  ],
  providers: [
    {provide: LOCALE_ID, useValue: 'no'
  ],
  imports: [
    BrowserModule,
    FormsModule,
    RouterModule.forRoot([
      { path: '', component: AppComponent }

    ])
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }


An example of using the locale setup is to be observed in the use of the pipe called currency. Sample markup:

  <td>{{ product.productCode | lowercase }}</td>
            <td>{{ product.releaseDate }}</td>
            <td>{{ product.price  | currency:'NOK':'symbol':'1.2-2' }}</td>
            <td>

Note the syntax here: Inside the interpolation expression in Angular with {{ My_Expression }} as the notation we pipe with the '|' sign to the currency pipe then we add a ':' sign and the 'NOK' denotes the Norwegian currency. If we want 'øre' (Norwegian cents) afterwards, we can add that outside the interpolation expression. Note that we also add another ':' sign and then 'symbol':'1.2-2' This states that we show at least one digit for the integer part and between 2 and 2 decimals (i.e. just 2 decimals please). This shows how we can set up the standard locale of an Angular app. Supporting multiple aplications should then be a matter of importing additional locales for Angular and have some way of switching between locales. Probably it is best to be sure to usually addition both the locale and the 'locale extra' when setting this up.

Friday, 2 August 2019

Misc OpenId Connect setup for Angular 8

This article presents some sample basic setup for OpenId Connect setup for Angular 8, targeting the npm library openid-client and Thinktecture IdentityServer 4 as the STS. The following AutService sets up the UserManager first on the client side. I must mention here that the setup ended up quite similar to the setup I did for an ASP.NET Core backend API I did for a React frontend SPA.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { UserManager, User, WebStorageStateStore, Log } from 'oidc-client';
import { Constants } from '../constants';
import { Utils } from './utils';
import { AuthContext } from '../model/auth-context';

@Injectable()
export class AuthService {
  private _userManager: UserManager;
  private _user: User;
  authContext: AuthContext;

  constructor(private httpClient: HttpClient) {
    Log.logger = console;
    const config = {
      authority: Constants.stsAuthority,
      client_id: Constants.clientId,
      redirect_uri: `${Constants.clientRoot}assets/oidc-login-redirect.html`,
      scope: 'openid projects-api profile',
      response_type: 'id_token token',
      post_logout_redirect_uri: `${Constants.clientRoot}?postLogout=true`,
      userStore: new WebStorageStateStore({ store: window.localStorage }),
      automaticSilentRenew: true,
      silent_redirect_uri: `${Constants.clientRoot}assets/silent-redirect.html`
    };
    this._userManager = new UserManager(config);
    this._userManager.getUser().then(user => {
      if (user && !user.expired) {
        this._user = user;
        this.loadSecurityContext();
      }
    });
    this._userManager.events.addUserLoaded(args => {
      this._userManager.getUser().then(user => {
        this._user = user;
        this.loadSecurityContext();
      });
    });
  }

  login(): Promise<any> {
    console.log('Inside the new auth service!');
    return this._userManager.signinRedirect();
  }

  logout(): Promise<any> {
    return this._userManager.signoutRedirect();
  }

  isLoggedIn(): boolean {
    return this._user && this._user.access_token && !this._user.expired;
  }

  getAccessToken(): string {
    return this._user ? this._user.access_token : '';
  }

  signoutRedirectCallback(): Promise<any> {
    return this._userManager.signoutRedirectCallback();
  }

  loadSecurityContext() {
    this.httpClient.get<AuthContext>(`${Constants.apiRoot}Account/AuthContext`).subscribe(context => {
      this.authContext = context;
    }, error => console.error(Utils.formatError(error)));
  }
}


The login redirect file oidc-login-redirect.html goes into the assets folder of Angular 8 project.
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.4.1/oidc-client.min.js"></script>
<script>
  var config = {
    userStore: new Oidc.WebStorageStateStore({ store: window.localStorage })
  };
  var mgr = new Oidc.UserManager(config);
  mgr.signinRedirectCallback().then(() => {
        window.history.replaceState({},
            window.document.title,
            window.location.origin);
        window.location = "/";
    }, error => {
        console.error(error);
    });

</script>

The silent redirect that automatically renews the token is in the file silent-redirect.html in the assets folder:
<script src="https://cdnjs.cloudflare.com/ajax/libs/oidc-client/1.5.1/oidc-client.min.js"></script>
<script>
  var config = {
    userStore: new Oidc.WebStorageStateStore({ store: window.localStorage })
  };
new Oidc.UserManager(config).signinSilentCallback()
        .catch((err) => {
            console.log(err);
        });

</script>
Then, the config in the ThinkTecture IdentityServer looks somewhat like this:
using System.Collections.Generic;
using IdentityServer4;
using IdentityServer4.Models;

namespace SecuringAngularApps.STS
{
    public class Config
    {
        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("projects-api", "Projects API")
            };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "spa-client",
                    ClientName = "Projects SPA",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    AllowAccessTokensViaBrowser = true,
                    RequireConsent = false,

                    RedirectUris =           { "http://localhost:4200/assets/oidc-login-redirect.html","http://localhost:4200/assets/silent-redirect.html" },
                    PostLogoutRedirectUris = { "http://localhost:4200/?postLogout=true" },
                    AllowedCorsOrigins =     { "http://localhost:4200/" },

                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "projects-api"
                    },
                    IdentityTokenLifetime=120,
                    AccessTokenLifetime=120

                },
                new Client
                {
                    ClientId = "mvc",
                    ClientName = "MVC Client",
                    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

                    ClientSecrets =
                    {
                        new Secret("secret".Sha256())
                    },

                    RedirectUris           = { "http://localhost:4201/signin-oidc" },
                    PostLogoutRedirectUris = { "http://localhost:4201/signout-callback-oidc" },

                    AllowedScopes =
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile
                    },
                    AllowOfflineAccess = true

                }
            };
        }

        public static IEnumerable<IdentityResource> GetIdentityResources()
            {
                return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };
            }
        }
    }
And in the API backend we wire up the STS like this in Asp.net Core in the Startup.cs:
using System;
using System.Linq;
using SecuringAngularApps.STS.Data;
using SecuringAngularApps.STS.Models;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authentication.Cookies;
using IdentityServer4.Services;
using SecuringAngularApps.STS.Quickstart.Account;

namespace SecuringAngularApps.STS
{
    public class Startup
    {
        public IConfiguration Configuration { get; }
        public IHostingEnvironment Environment { get; }

        public Startup(IConfiguration configuration, IHostingEnvironment environment)
        {
            Configuration = configuration;
            Environment = environment;
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy", corsBuilder =>
                {
                    corsBuilder.AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowAnyOrigin()
                    .AllowCredentials();
                });
            });
            services.AddTransient<IProfileService, CustomProfileService>();

            services.AddMvc();

            var builder = services.AddIdentityServer(options =>
                {
                    options.Events.RaiseErrorEvents = true;
                    options.Events.RaiseInformationEvents = true;
                    options.Events.RaiseFailureEvents = true;
                    options.Events.RaiseSuccessEvents = true;
                })
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients())
                .AddAspNetIdentity<ApplicationUser>()
                .AddProfileService<CustomProfileService>();


            if (Environment.IsDevelopment())
            {
                builder.AddDeveloperSigningCredential();
            }
            else
            {
                throw new Exception("need to configure key material");
            }
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseCors("CorsPolicy");

            app.UseIdentityServer();
            app.UseMvcWithDefaultRoute();
        }
    }
}

Thursday, 1 August 2019

Intellisense of spy objects (mocks) in Jasmin tests

When creating unit tests or integration tests for Angular 8, we often use mocking - such as mocking services. We sometimes want to fix up the intellisense of our mocks when we create a spy object using Jasmine (of which Angular tests most often are written in - the 'NUnit for Javascript world'). Here is how we can achieve that. First off, create a new file called Spied.ts and add this Typescript:
export type Spied<T> = {
  [Method in keyof T]: jasmine.Spy;
};
A little bit of terminology here for .NET coders concerning Javascript tests:
  • Spy object : Mock object
  • Using .and.returnValue(of(somedata)) : Equal to using Moq Setup method to return some data for given method
  • Expect in Jasmin : Similar to Assert in MSTest and NUnit.
This builds a mapped type that maps to a jasmine.Spy object, see the explanation of a mapped type here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#mapped-types We now can declare our mock objects as a 'Spied' object like this example:
let mockHeroService: Spied<HeroService>
mockHeroService = jasmine.createSpyObj(['getHeroes', 'addHero', 'deleteHero']);
The great thing about this then is that we now have decent Intellisense in place! Look at this video from VsCode as proof! To handle dependency injection scenarios do like in this example:
import { VoterService, ISession } from "src/app/events";
import { of } from "rxjs";
import { Spied } from "src/app/common/Spied";
import { HttpClient } from "@angular/common/http";

describe('VoterService', () => {
  let voterService: VoterService;
  let mockHttp: Spied<HttpClient>;

  beforeEach(() => {
    mockHttp = <Spied<HttpClient>>jasmine.createSpyObj('mockHttp', ['delete', 'post']);
    voterService = new VoterService(mockHttp);
    console.log('Inside beforeEach');
  });

  describe('deleteVoter', () => {

    it('should remove the voter from the list of voters', () => {
      var session = { id: 6, name: "John", voters: ["joe", "john"] };
      mockHttp.delete.and.returnValue(of(false));
      console.log(voterService);

      voterService.deleteVoter(3, <ISession>session, "joe");
      expect(session.voters.length).toBe(1);
    });

  });

});



We can then adjust our constructor to include the '| any' modifier of the injected parameter:

import { Injectable, Inject } from '@angular/core';
import { ISession } from '../shared';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class VoterService {
  constructor(@Inject(HttpClient) private http: HttpClient | any) {


  }

..

Note that we here adjust the constructor to not only accept the concrete class HttpClient but also 'any' allowing us to inject the mock object. We could alter this and introduce an interface for example instead for a more elegant approach. In case you get build errors like when running ng build stating that 'jasmine' could not be found, try out this: Inside tsconfig.json, explicitly add 'jasmine' for your 'types' like this:
{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "downlevelIteration": true,
    "experimentalDecorators": true,
    "module": "esnext",
    "moduleResolution": "node",
    "importHelpers": true,
    "target": "es2015",
    "types": [ "jasmine" ],
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2018",
      "dom"
    ]
  },
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  }
}

And then put the single line on top to import jasmine like this in Spied.ts:
import 'jasmine';