Wednesday, April 26, 2017

Immediately Invoked Function Expressions (IIFE) aka Self-executing Anonymous Functions

In the following html page, a heading shown. The color of the heading is changed to red by JavaScript. The scope of elem variable is global. If there is another global variable by the same name in any JavaScript included on this page, the elem will be destroyed and the script is not going to change the color of heading to red.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>IIFE</title>
</head>
<body>
<h1 id="heading">IIFE</h1>

<script>
var elem = document.getElementById("heading");
elem.style.color = "red";
</script>
</body>
</html>

In JavaScript, variables are either scoped globally (like above) or at function level. By wrapping the JavaScript on a page in an Immediately Invoked Function Expression (IIFE) (aka Self-executing Anonymous Function), we can limit the scope of elem variable to the function and avoid a name collision with an identically named variable in the global scope.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>IIFE</title>
</head>
<body>
<h1 id="heading">IIFE</h1>

<script>
(function (color){
var elem = document.getElementById("heading");
elem.style.color = color;
})("red");
</script>
</body>
</html>

Saturday, February 11, 2017

Creating a Google Chrome Extension

Google Chrome Extensions are created using ubiquitous HTML, JavaScript and CSS. Third-party JavaScript can be included and leveraged in building Chrome Extensions.

There are two types of Chrome extensions.

1) Browser Extension - Available at all times, with its icon displayed in the toolbar.

2) Page Extension - Applies to one or more pages. The icon is displayed in the address bar. Performs operations on the content of the page.

General Development Steps

1. Create an empty directory

2. Add the following files to the empty directory.

Manifest.json - Information about the extensions, permissions requested by the extension (for example, to access storage), and names of all the content files of the extension (html, javascript files).

3. Html files
May not include embedded javascript.

popup.html - file that is displayed when the extension icon is clicked, page title becomes the tooltip text for the extension icon
options.html (optional) - file for setting options for the extension

4. Icon
19x19 icon for the toolbar icon for the extension

5. JavaScript files
Normal javascript file referenced from the html pages

Event javascript page that is executed when an extension is loaded. Provides an opportunity to hook up handlers to various events (Event js is just background script with persistent set to false in manifest).
Background javascript file that keeps running in the background for the extension (persistent set to true).
Can have one or the other. 

Simply copy thirdparty libraries to the folder and reference in HTML file. Make reference to these files in the head tag of html using the script tag.

6. Css
Style files referenced from HTML pages.

For example, use popup.css in HTML page using the link tag. 

7. Images
Images referenced in HTML pages.

8. Content scripts 
Javascript that runs on the  page being browsed. Tacked on the page being viewed.
Cannot use chrome extensions API directly.
Must use messaging to communicate with chrome extension code.

9. Testing extension
In Google settings, go to Extensions tab.
Enable developer mode.
Load unpacked extension from the directory
Can do "Inspect Element" on the extension to debug.

Thursday, February 9, 2017

Creating Class Objects in JavaScript

Object Factory Pattern


  • A function that creates and returns a new object (o in code below) after it sets data and function members.
  • Members can be data values (e.g. radius) or functions (e.g. area(), circumference(), print()).
  • Members have access to other members using the this keyword (e.g. area() function accesses this.radius).
Source:

var CircleFactory = function(radius){
    var o = {};
    o.radius = radius;
    o.area = function(){
        return 22/7 * this.radius * this.radius;
    }
    o.circumference = function(){
        return 2 * 22/7 * this.radius;
    }
    o.print = function(){
        console.log("radius: " + this.radius + 
        " area: " + this.area() 
        + " circumference: " + this.circumference());
    }
    return o;
}

// Usage
var circle1 = CircleFactory(10);
circle1.print();
var circle2 = CircleFactory(15);
circle2.print();

Output:

radius: 10 area: 314.2857142857143 circumference: 62.857142857142854
radius: 15 area: 707.1428571428571 circumference: 94.28571428571428

Constructor Function Pattern

  • A normal function is defined. This can serve as the Constructor function (i.e. Circle). 
  • Members are defined using the this keyword. These members are publicly available. One member can access another member using the this keyword (e.g. area() function accesses this.radius).
  • Members can be data values (e.g. radius) or functions (e.g. area(), circumference(), print()).
  • An instance of a function (aka function object) is created using the new keyword.
  • Members are invoked on the function object (not on the Constructor function).
Source:
var Circle = function(radius) {
    this.radius = radius;

    this.area = function() {
        return 22 / 7 * this.radius * this.radius;
    }
    this.circumference = function() {
        return 2 * 22 / 7 * this.radius;
    }

    this.print = function() {
        console.log("radius: " + this.radius 
        + " area: " + this.area() 
        + " circumference: " + this.circumference());
    }
}

// Usage
var circle1 = new Circle(10);
circle1.print();
var circle2 = new Circle(15);
circle2.print();

Output:
radius: 10 area: 314.2857142857143 circumference: 62.857142857142854
radius: 15 area: 707.1428571428571 circumference: 94.28571428571428

Constructor Function using Prototype Pattern

  • First, see description of Constructor Function Pattern above. Constructor Function using Prototype Pattern is just a more efficient implementation of the Constructor Function Pattern.
  • Every new object constructed has it's own copy of radius, area(), circumference(), print() functions. The radius is going to vary from one function object to another so it makes sense that every new function object maintain it's own copy of radius. But the implementation of area(), circumference(), print() functions does not differ from one function object to another! When a large number of function objects are created, this duplication of member function code adds unnecessary memory overhead.
  • The prototype property on the function allows us to set a member of the function once and have it available for all new function objects.
  • Both data (PI) and functions (area(), circumference(), print()) can be defined on the prototype of a function object.
Source:
var Circle = function(radius) {
    this.radius = radius;
}

Circle.prototype.PI = 22/7;

Circle.prototype.area = function() {
    return this.PI * this.radius * this.radius;
}
Circle.prototype.circumference = function() {
    return 2 * this.PI * this.radius;
}
Circle.prototype.print = function() {
    console.log("radius: " + this.radius 
    + " area: " + this.area() 
    + " circumference: " + this.circumference());
}

// Usage
var circle1 = new Circle(10);
circle1.print();
var circle2 = new Circle(15);
circle2.print();

Output:
radius: 10 area: 314.2857142857143 circumference: 62.857142857142854
radius: 15 area: 707.1428571428571 circumference: 94.28571428571428

Monday, January 23, 2017

How to use GROUP BY and JOIN in LINQ

Demonstrates how to use GROUP BY and JOIN in LINQ

Please see comments inline with the code.

GitHub Repository: https://github.com/sal-razzaq/linq_groupby_join

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;

namespace LINQGroupByExample
{
    // Demonstrates GROUP BY and JOIN in LINQ
    [TestClass]
    public class LINQGroupByExampleTest
    {
        // ENTITY: Represents a Web Page Url
        class WebPage
        {
            public int Id { get; set; }
            public string Url { get; set; }
        }

        // ENTITY: Records an access of a Web Page by Url 
        class WebPageAccess
        {
            public int Id { get; set; }
            public string Url { get; set; }
            public DateTime AccessedAt { get; set; }
        }

        // ENTITY: Records latency experienced in accessing a
        // Web Page represented by Url
        class WebPageLatency
        {
            public int Id { get; set; }
            public string Url { get; set; }
            public int LoadTimeMs { get; set; }
        }

        // Test Data - These could be Entity Framework DbSets
        List<WebPage> WebPages = new List<WebPage>();
        List<WebPageAccess> WebPageAccesses = new List<WebPageAccess>();
        List<WebPageLatency> WebPageLatencies = new List<WebPageLatency>();

        // Test Urls
        const string URL1 = "abc.com";
        const string URL2 = "def.com";
        const string URL3 = "xyz.com";

        // Populate data for testing
        void SeedData()
        {
            // We are tracking three web pages represented
            // by their respective Urls
            this.WebPages.Add(new WebPage() { Url = URL1 });
            this.WebPages.Add(new WebPage() { Url = URL2 });
            this.WebPages.Add(new WebPage() { Url = URL3 });

            for (int i = 0; i < 10; i++)
            {
                // record 10 accesses for URL1
                this.WebPageAccesses.Add(new WebPageAccess() { Url = URL1 });
                // record 5 accesses for URL2
                if (i % 2 == 0)
                {
                    this.WebPageAccesses.Add(new WebPageAccess() { Url = URL2 });
                }

                // record a latency of 2 for URL1
                this.WebPageLatencies.Add(new WebPageLatency() { Url = URL1, LoadTimeMs = 2 });
                // record a latency of 4 for URL2
                if (i % 2 == 0)
                {
                    this.WebPageLatencies.Add(new WebPageLatency() { Url = URL2, LoadTimeMs = 4 });
                }
            }
            // no data recorded for URL3
        }

        const int ASSERT_URL1_TOTAL_VIEWS = 10;
        const int ASSERT_URL2_TOTAL_VIEWS = 5;
        const int ASSERT_URL1_AVG_LATENCY_MS = 2;
        const int ASSERT_URL2_AVG_LATENCY_MS = 4;

        [TestMethod]
        public void Test()
        {
            SeedData();

            // Group web page accesses by Url, count accesses by Url
            var webPageAccessGroups = from row in this.WebPageAccesses
                                      // we can group by multiple fields, if needed
                                      group row by new { row.Url } into urlAccessGroup
                                      select new                        // group projection
                                      {
                                          Url = urlAccessGroup.Key.Url,
                                          TotalViews = urlAccessGroup.Count()
                                      };

            // Group web page latency by Url, average latency by Url
            var webPageLatencyGroups = from row in this.WebPageLatencies
                                       group row by new { row.Url } into urlLatencyGroup
                                       select new                       // group projection
                                       {
                                           Url = urlLatencyGroup.Key.Url,
                                           AvgLatencyMs = urlLatencyGroup.Average(g => g.LoadTimeMs)
                                       };

            // Join the main table of Urls (WebPages) with their respective
            // access (view) counts and latencies
            var webPagesStatsByUrl = from webpage in this.WebPages
                                     join webPageAccessGroup in webPageAccessGroups on new { Url = webpage.Url }
                                     equals new { Url = webPageAccessGroup.Url } into webPageAccesses
                                     join webPageLatencyGroup in webPageLatencyGroups on new { Url = webpage.Url }
                                     equals new { Url = webPageLatencyGroup.Url } into webPageLatencies
                                     from webPageAccess in webPageAccesses.DefaultIfEmpty() // used below for projection
                                     from webPageLatency in webPageLatencies.DefaultIfEmpty() // used below for projection
                                     select new     // new projection
                                     {
                                         Url = webpage.Url,
                                         TotalViews = webPageAccess == null ? 0 : webPageAccess.TotalViews,
                                         AvgLatencyMs = webPageLatency == null ? 0 : webPageLatency.AvgLatencyMs
                                     };
            Debug.WriteLine("== Results ==");
            Debug.WriteLine("Url\t\t\tTotalViews\tAvgLatencyMs");
            foreach (var row in webPagesStatsByUrl)
            {
                Debug.WriteLine($"{row.Url}\t\t{row.TotalViews}\t\t\t{row.AvgLatencyMs}");
            }

            Assert.AreEqual(ASSERT_URL1_TOTAL_VIEWS, webPagesStatsByUrl.Where(w => w.Url == URL1).FirstOrDefault().TotalViews);
            Assert.AreEqual(ASSERT_URL2_TOTAL_VIEWS, webPagesStatsByUrl.Where(w => w.Url == URL2).FirstOrDefault().TotalViews);
            Assert.AreEqual(0, webPagesStatsByUrl.Where(w => w.Url == URL3).FirstOrDefault().TotalViews);

            Assert.AreEqual(ASSERT_URL1_AVG_LATENCY_MS, webPagesStatsByUrl.Where(w => w.Url == URL1).FirstOrDefault().AvgLatencyMs);
            Assert.AreEqual(ASSERT_URL2_AVG_LATENCY_MS, webPagesStatsByUrl.Where(w => w.Url == URL2).FirstOrDefault().AvgLatencyMs);
            Assert.AreEqual(0, webPagesStatsByUrl.Where(w => w.Url == URL3).FirstOrDefault().AvgLatencyMs);
        }
    }
}

Output:
== Results ==
Url TotalViews AvgLatencyMs
abc.com 10 2
def.com 5 4
xyz.com 0 0

Monday, January 16, 2017

Amazon API Gateway Velocity Template to Pass All Data to AWS Lambda

Lambda Integration Velocity Template

## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload


## allParams contains three members ($type below): path, querystring, header
## each member in turn is object with 0..n members

## stage-variables can defined for a particular "stage" of API Gateway

#set($allParams = $input.params())
{


"body-json" : $input.json('$'),

"params" : {
#foreach($type in $allParams.keySet())
#set($params = $allParams.get($type))
"$type" : {
#foreach($paramName in $params.keySet())
"$paramName" : "$util.escapeJavaScript($params.get($paramName))"
#if($foreach.hasNext),#end
#end
}
#if($foreach.hasNext),#end
#end
},


"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
#if($foreach.hasNext),#end
#end
},


"context" : {
"account-id" : "$context.identity.accountId",
"api-id" : "$context.apiId",
"api-key" : "$context.identity.apiKey",
"authorizer-principal-id" : "$context.authorizer.principalId",
"caller" : "$context.identity.caller",
"cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
"cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
"cognito-identity-id" : "$context.identity.cognitoIdentityId",
"cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
"http-method" : "$context.httpMethod",
"stage" : "$context.stage",
"source-ip" : "$context.identity.sourceIp",
"user" : "$context.identity.user",
"user-agent" : "$context.identity.userAgent",
"user-arn" : "$context.identity.userArn",
"request-id" : "$context.requestId",
"resource-id" : "$context.resourceId",
"resource-path" : "$context.resourcePath"
}
}

Data Received by AWS Lambda

{
"body-json": {
"appid": "myapp",
"eventtime": "2017-01-16",
"eventtype": "init",
"key1": "k1",
"key2": "k2",
"json": {
"astr": "str",
"anum": 123,
"bool": true
}
},
"params": {
"path": {},
"querystring": {},
"header": {
"Accept": "application/json",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "no-cache",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Content-Type": "application/json",
"Host": "5tt5f8xji6.execute-api.eu-west-1.amazonaws.com",
"Origin": "chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop",
"Postman-Token": "bb496633-fe87-142d-95ea-31d1ee34ebca",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
"Via": "1.1 64184371bdc0c4545cde799b5949d17c.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "k9f8O0GfedOrKH2UUI10lt0QHBMbsPGyz-0GdwJpTWasNB_n9fMdgA==",
"X-Forwarded-For": "23.120.xx.xxx, 54.240.xxx.xx",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
}
},
"stage-variables": {},
"context": {
"account-id": "",
"api-id": "5tt5f8xji6",
"api-key": "",
"authorizer-principal-id": "",
"caller": "",
"cognito-authentication-provider": "",
"cognito-authentication-type": "",
"cognito-identity-id": "",
"cognito-identity-pool-id": "",
"http-method": "POST",
"stage": "prod",
"source-ip": "23.120.xx.xxx",
"user": "",
"user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36",
"user-arn": "",
"request-id": "f5ab9082-dc73-11e6-8ca5-15a998eb4e77",
"resource-id": "kmtrw2",
"resource-path": "/trace"

}
}

Thursday, January 5, 2017

Text to speech conversion using Amazon Polly and .Net

Nuget Package

AWSSDK.Polly

Code

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Amazon.Polly;
using Amazon.Polly.Model;
using Amazon;
using System.IO;

namespace PollyTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
AmazonPollyClient client = new AmazonPollyClient(RegionEndpoint.USWest2);

DescribeVoicesRequest voiceRequest = new DescribeVoicesRequest();
DescribeVoicesResponse voiceResponse = client.DescribeVoices(voiceRequest);
var voicesList = voiceResponse.Voices;
SynthesizeSpeechRequest synthesizeRequest = new SynthesizeSpeechRequest()
{
Text = "Well, that was easy!",
VoiceId = voicesList[0].Id,
OutputFormat = OutputFormat.Mp3
};
var synthesizeResponse = client.SynthesizeSpeech(synthesizeRequest);

byte[] mp3Bytes = ReadToEnd(synthesizeResponse.AudioStream);
File.WriteAllBytes(@"C:\temp\easy.mp3", mp3Bytes);
}

public static byte[] ReadToEnd(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
}
}

Wednesday, December 7, 2016

Takeaways from re:Invent 2016 Amazon Web Services Conference

I attended the Amazon Web Services conference last week. Here are my main takeaways.

AWS is introducing a large number of higher level services. Amazon Web Services now spans the entire spectrum from IaaS (Infrastructure as Service,  e.g. EC2 VMs and VPCs) to SaaS (Software as a Service, e.g. hosted machine learning). The main themes of this year's conference were serverless computing and machine learning (Artificial Intelligence).

AWS has settled on three computing models: EC2 instances (VMs you provision and manage), Containers (Fully-managed Docker containers you run your micro services in an isolated manner) and Lambda (code you just upload to AWS and run; AWS does all the provisioning and scaling). Lambda now supports C#, in addition to node.js, Java, and Python. We have been successfully using Lambda (Java implementation) since the beginning of this year. Having C# as an option, makes Lambda more useful for us since we can now refactor and reuse our current C# code.

With respect to machine learning, AWS introduced Rekognition (hosted image recognition service), Polly (speech to text engine), and Lex (text to speech engine). This provides the entire stack for creating speech-based systems. With the success of Amazon Echo (Alexa), I think speech-based systems are going to be big in 2017.

It appears relational database systems are still the storage solution of choice for most companies. AWS Aurora (a MySql-like hosted database service) was extensively covered. It is apparently Amazon's fastest growing service. There wasn't as much talk of DynamoDb (NoSql) database this year.