Angular Directives - Numbers only Directive

 Angular Directives

In Angular, Directives are custom HTML attributes which tell Angular compiler to change the style or behavior of the elements. These are basically classes, that add additional behavior to elements in your Angular applications.

We have 3 different types of Directives in Angular

  1. Component Directives: These are directives with a template. General components 
  2. Attribute Directives: These will change the appearance and behavior of the elements. Built in Attribute directives are NgClass, NgStyle, NgModel etc.
  3. Structural Directives: These will change the DOM layout by adding or removing DOM elements. Built in Attribute directives are NgIf, NgFor, NgSwitch etc.

You can read more about directives here.

Creating Numbers only Directive

In this post, I am going to explain how to create an attribute Directive which will allow only numbers from the user for an input box.

1) Create a directives by using below command  
ng generate directive numbersOnly
which generates 
1) A directive file src/app/numbers-only.directive.ts
2) A test file for the above directive src/app/numbers-only.directive.spec.ts
3) Declare the class in app.module.ts
with the below content

src/app/numbers-only.directive.ts
import { Directive } from '@angular/core';

@Directive({
  selector: '[appNumbersOnly]'
})
export class NumbersOnlyDirective {

  constructor() { }

}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NumbersOnlyDirective } from './numbers-only.directive';

@NgModule({
  declarations: [
    AppComponent,
    NumbersOnlyDirective
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
2) Now add ElementRef in the directive's constructor() to inject a reference to the host DOM element, the element to which you apply appNumbersOnly
import { Directive, ElementRef } from '@angular/core';

@Directive({
  selector: '[appNumbersOnly]'
})
export class NumbersOnlyDirective {

  inputElement: HTMLElement;

  constructor(public el: ElementRef) {
    this.inputElement = el.nativeElement;
  }
}
3) Now for implementing the numbers only we need to handle in 3 ways
a) When user enters a key
b) When user paste some value
c) When user drop some value
  @HostListener('keydown', ['$event'])
  onKeyDown(e: KeyboardEvent) {
    
    // Ensure that it is a number and stop the keypress
    if (!isNaN(+e.key)) {
      return;
    }
    else {
      e.preventDefault();
    }
  }

  @HostListener('paste', ['$event'])
  onPaste(event:any) {
    event.preventDefault();
    const pastedInput: string = event.clipboardData
      .getData('text/plain')
      .replace(/[^0-9-]+/ig, ''); // get a digit-only string
    document.execCommand('insertText', false, pastedInput);
  }

  @HostListener('drop', ['$event'])
  onDrop(event: DragEvent) {
    event.preventDefault();
    const textData =  event.dataTransfer?.getData('text').replace(/[^0-9-]+/ig, '');
    this.inputElement.focus();
    document.execCommand('insertText', false, textData);
  }
Here the in the keydown event I am checking whether e.Key is number or not. 
Note: e.KeyCode,e.which are deprecated and not supported from Angular 8 version. Read more here.

4) Now our directive will accept only numbers. Here our keydown event accept only if the given key is number. But we need to allow some other keys as well like Backspace, Delete, Tab etc. So add exceptions for those as well.
 private navigationKeys = [
    'Backspace',
    'Delete',
    'Tab',
    'Escape',
    'Enter',
    'Home',
    'End',
    'ArrowLeft',
    'ArrowRight',
    'Clear',
    'Copy',
    'Paste'
  ];
@HostListener('keydown', ['$event'])
  onKeyDown(e: KeyboardEvent) {
    if (
      this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
      (e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
      (e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
      (e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
      (e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
      (e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
      (e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
      (e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
      (e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
    ) {
      // let it happen, don't do anything
      return;
    }
    
    // Ensure that it is a number and stop the keypress
    if (!isNaN(+e.key)) {
      return;
    }
    else {
      e.preventDefault();
    }
  }
  
  
Here is the final version of our Directive.
 import { Directive, ElementRef, HostListener } from '@angular/core';

@Directive({
  selector: '[appNumbersOnly]'
})
export class NumbersOnlyDirective {
  private navigationKeys = [
    'Backspace',
    'Delete',
    'Tab',
    'Escape',
    'Enter',
    'Home',
    'End',
    'ArrowLeft',
    'ArrowRight',
    'Clear',
    'Copy',
    'Paste'
  ];

  inputElement: HTMLElement;

  constructor(public el: ElementRef) {
    this.inputElement = el.nativeElement;
  }

  @HostListener('keydown', ['$event'])
  onKeyDown(e: KeyboardEvent) {
    if (
      this.navigationKeys.indexOf(e.key) > -1 || // Allow: navigation keys: backspace, delete, arrows etc.
      (e.key === 'a' && e.ctrlKey === true) || // Allow: Ctrl+A
      (e.key === 'c' && e.ctrlKey === true) || // Allow: Ctrl+C
      (e.key === 'v' && e.ctrlKey === true) || // Allow: Ctrl+V
      (e.key === 'x' && e.ctrlKey === true) || // Allow: Ctrl+X
      (e.key === 'a' && e.metaKey === true) || // Allow: Cmd+A (Mac)
      (e.key === 'c' && e.metaKey === true) || // Allow: Cmd+C (Mac)
      (e.key === 'v' && e.metaKey === true) || // Allow: Cmd+V (Mac)
      (e.key === 'x' && e.metaKey === true) // Allow: Cmd+X (Mac)
    ) {
      // let it happen, don't do anything
      return;
    }
    
    // Ensure that it is a number and stop the keypress
    if (!isNaN(+e.key)) {
      return;
    }
    else {
      e.preventDefault();
    }
  }

  @HostListener('paste', ['$event'])
  onPaste(event:any) {
    event.preventDefault();
    const pastedInput: string = event.clipboardData
      .getData('text/plain')
      .replace(/[^0-9-]+/ig, ''); // get a digit-only string
    document.execCommand('insertText', false, pastedInput);
  }

  @HostListener('drop', ['$event'])
  onDrop(event: DragEvent) {
    event.preventDefault();
    const textData =  event.dataTransfer?.getData('text').replace(/[^0-9-]+/ig, '');
    this.inputElement.focus();
    document.execCommand('insertText', false, textData);
  }
  
}
 

Applying our custom attribute directive

To use the NumbersOnlyDirective, add an <input> element to the HTML template with the directive as an attribute.
 Enter number:

Creating Custom Elements using Angular

Custom Elements is one of the key features of the Web platform. Custom Elements extend HTML by allowing you to define a tag whose content is created and controlled by JavaScript code. The idea is that you can use something like <app-chat-form></app-chat-form> along with the standard elements like <h1>,<input> etc. This feature currently supported by Chrome, Edge (Chromium-based), Firefox, Opera, and Safari, and available in other browsers through polyfills. You can learn more about custom elements here and here.

Angular Elements allow us to expose our components as reusable elements. We can use the Angular elements in other angular applications or non-angular applications such as HTML, React, PHP, WordPress etc. as well. In general Angular Elements are Angular Components packaged as custom elements. @angular/elements package exports a createCustomElement() API that provides a bridge from Angular's component interface and change detection functionality to the built-in DOM API. It automatically converts your component to equivalent native HTML elements.

Following are the Step by Step process for creating custom elements with Angular. Here I am creating a simple contact form.

1) Create an Angular project

Create an Angular project in CLI using below command
  ng new angular-chat-form --routing=false --skip-tests=true --style=css
I am not using the routing and tests for my project and using css for styling

2) Create a chat component

Lets create an angular component which will turn as embeddable web component later.
ng generate component chat-form
or 
ng g c chat-form
This will generate the typescript, html template and css files for the component and below are the code blocks in each file. You can add your own design and code here.

chat-form.component.html
{{title}}
Name is required
Email is required

chat-form.component.css
* {box-sizing: border-box;}

/* Button used to open the contact form - fixed at the bottom of the page */
.open-button {
  cursor: pointer;
  position: fixed;
  bottom: 0px;
  right: 28px;
  width: 280px;
}

/* The popup form - hidden by default */
.form-popup {
  position: fixed;
  bottom: 0;
  right: 15px;
  z-index: 9;
  width: 400px;
}

chat-form.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-chat-form',
  templateUrl: './chat-form.component.html',
  styleUrls: ['./chat-form.component.css']
})
export class ChatFormComponent implements OnInit {

  @Input() id: number = -1;
  @Input() title: string = "Sign Up! for News Letter";
  @Input() buttonTitle: string = "Subscribe for News Letter";
  formOpen = false;
  formData = { name: '', email: '' };
  formSubmitted = false;

  constructor() { }

  ngOnInit() {

  }
  openForm() {
    this.formData = { name: '', email: '' };
    this.formSubmitted = false;
    this.formOpen = true;
  }
  onSubmit(form: NgForm) {
    this.formSubmitted = true;
    if (!form.valid)
      return;
  }
  closeForm() {
    this.formOpen = false;
  }
}
Now my component is ready. Here I used bootstrap for the design in my application. 

3) Add the Angular Elements package

We need to get the @angular/elements library, so we run
ng add @angular/elements 
This will bring the library inside our node_modules folder. It also adds the document-register-element.js polyfill required for web browsers that don't support custom elements yet and the @angular/elements package.

Now we need to tell Angular, not to treat our component as common Angular component. We can do in it in the module bootstrapping level. For this, we need to implement ngDoBootstrap method in our AppModule and tell our module to define a custom element by using the createCustomElement function in @angular/elements package. Below is how my app.module.ts looks like after these changes (Removed the AppComponent). 

app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { DoBootstrap, Injector, NgModule } from '@angular/core';

import { ChatFormComponent } from './chat-form/chat-form.component';
import { FormsModule } from '@angular/forms';
import { createCustomElement } from '@angular/elements';

@NgModule({
  declarations: [
    ChatFormComponent,
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  entryComponents: [ChatFormComponent]
})
export class AppModule implements DoBootstrap {
  constructor(private injector: Injector) {
    const customElement = createCustomElement(ChatFormComponent, { injector });
    customElements.define('app-chat-form', customElement);
  }

  ngDoBootstrap() {

  }
} 
Here,
  1. Add ChatFormComponent inside entryComponents array property of NgModule and remove the AppComponent
  2. We need to pass the injector to our component manually to ensure dependency injection works at runtime.
  3. We put our component in entryComponents list for bootstrapping the web component.  
  4. createCustomElement turns our component to web component. We provide the output of this function to customElements.define function.
  5. customElements.define tells how our component can be called from other HTML pages. We can override our Angular component selector here. In this case, I used the same name but we can use any two or more words separated with hyphen (as per Custom Elements API, name should contains at least one hyphen. Check here for more details. 
    customElements.define('chat-form', customElement);
    
  6. Finally we are using ngDoBootstrap() to manually bootstrap the app module.


4) Build the application

Our next step is to build the application. 
ng build
At this point, we can find the following files in dist folder.


You will not get the vendor.js file if you build for prod as below.
ng build --prod --output-hashing=none
in this case, your dist folder will look like below









5) Using the custom element in HTML page

We need to include these files in app where we need to use our custom element as below.


	
	AngChatForm
	
	


	

Angular Elements demo

Note: 
  1. We need to add bootstrap CDN as we used bootstrap npm
  2. The input elements naming pattern is different for custom elements. When we use Camel case (buttonTitle) in our Angular element input variable, then we need to use Kebab case (button-title)

It will work as below

Chrome





IE 11




Exporting the element as Single .js File

Generally, when we run the ng build or ng build -prod, it will create different files runtime, main, polyfills, vendor etc files in dist folder and we need to refer all these files in our target application. For convenience, we can bundle our Angular element as a single .js file using jscat npm. Install the jscat library as below.
npm install jscat
Modify the build command in package.json to build our app for prod mode. Make hashing off, so that we will get the files with standard names like main, polyfills etc.
Add another command called package, package2015 to concatenate all the required script files into 1 script file called ang-chat-button.js  file inside script section. 

Now my package.json scripts section will looks like as below
 "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build --prod --output-hashing=none",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "package": "jscat ./dist/ang-chat-form/runtime-es5.js ./dist/ang-chat-form/polyfills-es5.js ./dist/ang-chat-form/main-es5.js ./dist/ang-chat-form/vendor-es5.js > ./dist/ang-chat-button.js",
    "package2015": "jscat ./dist/ang-chat-form/runtime-es2015.js ./dist/ang-chat-form/polyfills-es2015.js ./dist/ang-chat-form/main-es2015.js ./dist/ang-chat-form/vendor-es2015.js > ./dist/ang-chat-button-es2015.js"
  }
Now just run the below command
 
npm run build && package
or (for ES6 support)
 
npm run build && package2015
It will build the application and create a file called ang-chat-button.js in "dist" folder.




Now you can use this one file in your target application along with the css files. The above html is now simplified as 
 



	
	AngChatForm
	
	


	

Angular Elements demo

You can find the code created for this post here.
Happy Coding 😀!



Handle Twilio SMS Status Call back in Azure Logic apps

In my previous post, I explained how to send SMS using Twilio connector in Azure logic app. In this post, I will describe how to capture the Twilio SMS Status call back events after we sent the SMS using Twilio and save them to the database using logic app . 

Prerequisites:

  • Azure subscription
  • A sender application to send SMS using Twilio

Steps:

1) Login to Azure portal

2) Select "Create a Resource" -> Search for Logic app -> Create

3) Select subscription, resource group, Region and give logic app name -> Click on Review & Create button -> Create

4) Open the logic app once the deployment completed

5) Select Http Request response template in the Logic Apps Designer. It will create a new app with Request and Response connectors.

6) I want to capture the SMS status back to the SQL server using SQL connector. Insert new step(+ icon) after Http Trigger connector -> Add an Action -> In the search box, enter "Sql" to filter the connectors -> Select "Sql Serve" -> Under the actions list select the "Insert row (V2)"

7) Create the connection by giving the required details 

8) Once you created the connection, select the Server name, Database name, table name. Once you          selected the table name, it will show the required columns name automatically in the form. If you want other columns, you can open the "Add New Parameter" drop down and select the required columns. 








9) Here I want to capture the MessageId (Unique message Id in our system), MessageSid (Sid from Twilio), MessageStatus (Status from Twilio) from the call back data. As I am sending the MessageId as query string parameter to the call back page, I am capturing it's value by using the expression 

 @triggerOutputs()['queries']['Id']

The main thing here is Twilio will post the data to our call back end point in application/x-www-form-urlencoded format. So I used the below expression to get the parameters posted by Twilio. We can red the form data posted to our logic app with the below expression

triggerFormDataValue('MessageSid')

triggerFormDataValue('MessageStatus')

10) In the Response connector, just send the 200 status code back to Twilio.






11) Save the logic app. Once you saved the logic app it will generate an URL in the HTTP trigger connector.

https://prod-13.eastus.logic.azure.com:443/workflows/5689fd744182e381f19a1a7ed4ee58cf/triggers/manual/paths/invoke?api-version=2016-10-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=ZrrSXg_0ZY5_hXz_yft0BPYiyXdWKK8g-ji7Ldm1Hb8

12) Use this URL as Status call back urn while sending the Twilio SMS along with the query string value id. Below is how I used in the logic app created in my previous post.




        



                                



 That's it. Now your logic app will receive the status call backs and save the response in the database.

Happy Coding 😀!

Azure Logic Apps - Sending SMS using Twilio connector

 Azure logic apps is a cloud service that helps you to define your works flows, schedule tasks etc in the cloud. It is server less and fully managed IPAAS (Integration Platform as Service) with inbuilt scalability. 

Logic apps provides a visual designer for create and configure your work flows. You can define your work flow with in-built connectors or other enterprise connectors. 

Advantages of Logic app

  • Inbuilt Connectors: We have plenty of inbuilt connectors, triggers and actions that cover many of the common business scenarios. 
  • Templates: Logic apps comes with many predefined templates that are available for common business cases which reduces the  development work.
  • Integration: You can integrate your on-premise legacy systems with the new ones in the cloud. 
  • Extensibility: You can create your own custom APIs, Azure Functions and integrate them to the workflow, if the inbuilt connectors are not met your needs.
  • Minimal Development Effort:  We can create the logic apps with minimal development effort in browser or visual studio.

Logic app for sending an SMS

Azure logic apps offers a pre-built Twilio connector which can allow you to send, get or list SMS / MMS from your Twilio account. In this post I will demonstrate how to use the logic app to Send an SMS using Twilio connector.

Prerequisites:

  • Azure subscription
  • Twilio AccountId and Authentication token
  • A Twilio number / Twilio verified number to send SMS
Steps:
1) Login to Azure portal
2) Select "Create a Resource" -> Search for Logic app -> Create
3) Select subscription, resource group, Region and give logic app name -> Click on Review & Create button -> Create
4) Open the logic app once the deployment completed
5) Select Http Request response template in the Logic Apps Designer. It will create a new app with Request and Response connectors as below


6) I want to send the SMS request in the following format
{
    "sendingNumber": "xxxxxxxxx",
    "destinationNumber": "xxxxxxxxx",
    "messageContent": "message from logic app",
    "statusCallBackURL": "https://hookb.in/yDRKx6el2xFJNNPaR2kk"
}
So, in the Http request trigger, give the following as request body JSON schema
{
    "type": "object",
    "properties": {
        "sendingNumber": {
            "type": "string"
        },
        "destinationNumber": {
            "type": "string"
        },
        "messageContent": {
            "type": "string"
        },
        "statusCallBackURL": {
            "type": "string"
        }
    }
}



7) Click on Insert new step(+ icon) after Http Trigger connector -> Add an Action -> In the search box, enter "twilio" to filter the connectors -> Under the actions list select the action you want. I selected Send Text Message (SMS) as we are going to send a message.



8) Now provide the necessary details for your connection like Connection name, Twilio Account Id, Twilio Access Token and select Create button










9) Give the necessary details for sending the SMS like From Phone Number, To Phone Number, Text, Status Call back (You can get this parameter by selecting it from "Add New Parameter" dropdown). You can pick these values from logic app Trigger.













10) It completes the message sending. But I want to send the message SID back to the user in response. So I modified the Response connector as below












11) Save the logic app. Once you saved the logic app it will generate an URL in the HTTP trigger connector.  Now you can trigger your logic app by posting your request to that URL.

I will explain the logic app for handling the Twilio SMS status call back in another post. 

Happy Coding 😀!

Streaming/ Playing an audio file from FTP

In General FTP doesn't allow to stream a file directly from FTP site. In this post, I will explain how, I did it in an ASP.NET project to play the audio file from FTP. My aim is to play an audio file which is in FTP using HTML audio tag without downloading the file to local folder (server folder). So as FTP will not allow direct streaming, I created an HttpHandler under my ASP.NET project to stream the file. My handler will read the file queried from the FTP using FtpWebRequest and send it back to the webpage. Following the code for my handler (GetAudio.ashx)
<%@ WebHandler Language="C#" Class="GetAudio" %>

using System;
using System.Web;
using System.Configuration;
using System.Net;
using System.IO;

public class GetAudio : IHttpHandler
{

    string ftpServer = ConfigurationManager.AppSettings["FTPServerForMRVPrompts"].ToString();
    string ftpUsername = ConfigurationManager.AppSettings["FTPServerForMRVPromptsUserName"].ToString();
    string ftpPassword = ConfigurationManager.AppSettings["FTPServerForMRVPromptsPassword"].ToString();

    public void ProcessRequest(HttpContext context)
    {
        string path = context.Request.QueryString["path"];
        string filePath = "ftp://" + ftpServer + path.ToString();
        try
        {
            var fineName = Path.GetFileName(filePath);
            FtpWebRequest ftpWebRequest = null;
            ftpWebRequest = (FtpWebRequest)FtpWebRequest.Create(filePath);
            // Get get the actual data            
            ftpWebRequest.Credentials = new NetworkCredential(ftpUsername, ftpPassword);
            ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
            ftpWebRequest.UsePassive = true;
            ftpWebRequest.UseBinary = true;
            ftpWebRequest.KeepAlive = false; //close the connection when done
                                           
            FtpWebResponse response = (FtpWebResponse)ftpWebRequest.GetResponse();
            context.Response.Clear();
            context.Response.Buffer = true;
            context.Response.AddHeader("content-disposition",
                string.Format("application; filename=\"{0}\"", fineName));
            context.Response.Cache.SetCacheability(HttpCacheability.Public);
            context.Response.Cache.SetMaxAge(new TimeSpan(DateTime.Now.AddMinutes(30).ToFileTime()));
            context.Response.ContentType = "audio/wav";

            byte[] buffer = new byte[1024];
            int bytes;
            Stream outStream = context.Response.OutputStream;
            using (Stream stream = response.GetResponseStream())
            {
                while ((bytes =
                    stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    outStream.Write(buffer, 0, bytes);
                }
            }
        }
        catch
        {
           
        }
        context.Response.End();
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }

}
And in my web page, I am calling my handler as below

That's it.

Here I used Response caching in my handler with max-age of 30 mins, to enforce the file caching at browser.

Happy Coding 😊!

Enable TLS 1.2 on Windows 7

Transport Layer Security (TLS) is the next generation for Secure Socket Layer (SSL) protocal. It provides secure communication over internet. TLS is used by Web Browsers and other applications which require data to be securely exchanged over a network such as VPN, email, FTP etc.

Windows 7 support TLS 1.1 and TLS 1.2, but those are not enabled by default. Those are enabled by default in later versions such as Windows 8, 10. We can enable it in Windows 7 by using the following steps


  1. Open registry editor ( By using "regedit" command in Run)
  2. Go to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols
  3. Right click on "Protocols" and Select New -> Key. It will create "New Key #1" under "Protocols"
  4. Right click on "New Key #1" and Select Rename. Rename it to "TLS 1.2"
  5. Right click on "TLS 1.2" and Select New -> Key. It will create "New Key #1" under "TLS 1.2"
  6. Right click on "New Key #1" and Select Rename. Rename it to "Client"
  7. Right click on "Client" and Select New -> "DWORD (32-bit) value". It will create new file in the right pane with name "New Value #1". 
  8. Right click on "New Value #1" and Select Rename. Rename it to "DisabledByDefault"
  9. If you are using the machine as local IIS server for development server, then 
    • Right click on "TLS 1.2" and Select New -> Key. It will create another "New Key #1" under "TLS 1.2"
    • Right click on "New Key #1" and Select Rename. Rename it to "Server"
    • Right click on "Server" and Select New -> "DWORD (32-bit) value". It will create new file in the right pane with name "New Value #1".
    • Right click on "New Value #1" and Select Rename. Rename it to "DisabledByDefault"
  10. Close the editor and reboot the machine.
Happy Coding 😊!

Angular: Get Comma separated string for one Property from an Array of Objects

This is a small useful snippet which will explain how to get a comma separated string for one property from an array of objects.

If following is your array of objects
var usersArray = [
    {
        id: "1",
        name: "Ram",
        active: true
    },
    {
        id: "2",
        name: "Jishith",
        active: false
    },
    {
        id: "3",
        name: "Anand",
        active: true
    },
    {
        id: "4",
        name: "Aayush",
        active: false
    }
];

To get the name as comma separated string, you just need to write as
var userNames = users.map(s => s.name).toString();
console.log(userNames);
The output will be
Ram,Jishith,Anand,Aayush

Suppose, if you want to get only active user list
var userNames = users.filter(s => s.active == true).map(s => s.name).toString();
console.log(userNames);
The output will be
Ram,Anand
If you want to filter with an array of values for an property, you have to use as below
var userIds=[2,3,4]
var userNames = users.filter(function (item) {
          return this.userIds.includes(item.id)
        }).map(s => s.name).toString(); 
console.log(userNames);
The output will be
Jishith,Anand,Aayush
Happy Coding 😊!

Deploy Azure function using Azure CLI

The Azure command-line interface (Azure CLI) is a set of commands used to create and manage Azure resources. It is available across Azure services and is designed to get you working quickly with Azure, with an emphasis on automation. You can check how to install and configure Azure CLI in you machine from here.

This article describes how to deploy your function app to Azure using AWS CLI commands. For deploying the azure fuction you need to first create a zip file with the contents of your azure function. The .zip file that you use for push deployment must contain all of the files needed to run your function. When you deploy it, the contents of the zip file will be extracted and copied to the wwwroot folder of your function app(If any files from an existing deployment that aren't found in the .zip file are deleted from your function app). The folder structure of your zip file should be as below

FunctionApp
 | - host.json
 | - MyFirstFunction
 | | - function.json
 | - MySecondFunction
 | | - function.json
 | - SharedCode
 | - bin


The code for all the functions in a specific function app is located in a root project folder that contains a host configuration file and one or more sub folders. Each sub folder contains the code for a separate function. The bin folder contains packages and other library files that the function app requires. 

 So for deploying your azure function app, you need to publish your app first in Visual Studio using folder publish. Once done, zip the publish folder and run the following command in command line

az functionapp deployment source config-zip -g  -n  --src 

Ex:
az functionapp deployment source config-zip -g "rg-test-group" -n "func-test-function" --src "D:\test-function\bin\Release\netcoreapp2.1\publish\publish.zip"

This command deploys project files from the .zip file to your function app and restarts it. Here is the path of your zip file on your computer. If you are using Azure CLI in Azure Cloud Shell, then you must upload your file to the Azure files account.

Happy Coding 😊!

Deploying Azure function With ARM Template

ARM Template

In agile development methodology, we need to repeatedly deploy our solution to the cloud. So we need to automate the deployment process and use the practice of Infrastructure as code. In our code, you can define the infrastructure that needs to be deployed. The infrastructure code is part of our code and we can store it in the repo and version it. 

In Azure we can use the Azure Resource Manage templates to implement the Infrastructure as code concept. The ARM template is a Json file that defines the infrastructure and configuration for your project. In the template, you specify the resources to deploy and the properties for those resources. 

Advantages of ARM Template

Following are the main advantages of using ARM Template
  1. Declarative Syntax: The template will deploy any infrastructure like VM, network infrastructure, storage systems etc
  2. Repeatable results: Repeatedly deploy your infrastructure throughout the development lifecycle and have confidence your resources are deployed in a consistent manner
  3. Built in validation: Resource Manager checks the template before starting the deployment to make sure the deployment will succeed.
  4. CI/CD integration: You can integrate templates into your continuous integration and continuous deployment (CI/CD) tools, which can automate your release pipelines for fast and reliable application and infrastructure updates. 
  5. Tracked deployments: In the Azure portal, you can review the deployment history and get information about the template deployment.

ARM Template Format

A template has the following elements
  1. $schema: Location of the JSON schema file that describes the version of the template language.
  2. contentVersion: Version of the template.
  3. apiProfile: An API version that serves as a collection of API versions for resource types
  4. parameters: Values that are provided when deployment is executed to customize resource deployment.
  5. variables: Values that are used as JSON fragments in the template to simplify template language expressions.
  6. functions: User-defined functions that are available within the template.
  7. resources: Resource types that are deployed or updated in a resource group or subscription.
  8. outputs: Values that are returned after deployment.
Here $schema, contentVersion, resources  are mandatory elements for any template
We can write template expressions that extend the capabilities of JSON file.

ARM Template for deploying Azure Function

{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
  "parameters": {
    "environtment": {
      "defaultValue": "dev",
      "type": "String"
    },
    "storageSKU": {
      "defaultValue": "Standard_LRS",
      "allowedValues": [
        "Standard_LRS",
        "Standard_GRS",
        "Standard_RAGRS",
        "Standard_ZRS",
        "Premium_LRS",
        "Premium_ZRS",
        "Standard_GZRS",
        "Standard_RAGZRS"
      ],
      "type": "String"
    },
    "dbConnectionString": {
      "defaultValue": "",
      "type": "String"
    },
    "packageUrl": {
      "defaultValue": "https://gopiportal.blob.core.windows.net/code/AzureFunctions.zip",
      "type": "String"
    }
    
  },
    "variables": {
        "storageAccountName": "[concat('gopitest',parameters('environtment'),'sa')]",
        "servicePlanName": "[concat('gopitest-',parameters('environtment'),'-sp')]",
        "azureFunctionAppName": "[concat('gopitest-',parameters('environtment'),'-af')]",
        "appInsightsName": "[concat('gopitest-',parameters('environtment'),'-ai')]"
    },
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-04-01",
            "name": "[variables('storageAccountName')]",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "[parameters('storageSKU')]"
            },
            "kind": "StorageV2"
        },
        {
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2018-02-01",
            "name": "[variables('servicePlanName')]",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "Y1",
                "tier": "Dynamic"
            },
            "properties": {
                "name": "[variables('servicePlanName')]",
                "computeMode": "Dynamic"
            }
        },
        {
            "type": "Microsoft.Insights/components",
            "apiVersion": "2015-05-01",
            "name": "[variables('appInsightsName')]",
            "location": "[resourceGroup().location]",
            "tags": {
                "[concat('hidden-link:', resourceGroup().id, '/providers/Microsoft.Web/sites/', variables('azureFunctionAppName'))]": "Resource"
            },
            "properties": {
                "ApplicationId": "[variables('azureFunctionAppName')]",
                "Application_Type": "web"
            }
        },
        {
            "type": "Microsoft.Web/sites",
            "apiVersion": "2019-08-01",
            "name": "[variables('azureFunctionAppName')]",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
                "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
    "[resourceId('Microsoft.Storage/components', variables('appInsightsName'))]"
            ],
            "kind": "functionapp",
            "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
                "siteConfig": {
                  "appSettings": [
                    {
                      "name": "AzureWebJobsDashboard",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
                    },
                    {
                      "name": "AzureWebJobsStorage",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
                    },
                    {
                      "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
                      "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
                    },
                    {
                      "name": "WEBSITE_CONTENTSHARE",
                      "value": "[variables('storageAccountName')]"
                    },
                    {
                      "name": "FUNCTIONS_EXTENSION_VERSION",
                      "value": "~2"
                    },
                    {
                      "name": "FUNCTIONS_WORKER_RUNTIME",
                      "value": "dotnet"
                    },
                    {
                      "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                      "value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
                    },
                    {
                      "name": "dbConnectionString",
                      "value": "[parameters('reportingDBConnectionString')]"
                    }
                  ]
                }
            },
            "resources": [
                {
                    "type": "extensions",
                    "apiVersion": "2015-08-01",
                    "name": "MSDeploy",
                    "location": "[resourceGroup().location]",
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites', variables('azureFunctionAppName'))]"
                    ],
                    "tags": {
                        "displayName": "webappdeploy"
                    },
                    "properties": {
                        "packageUri": "[parameters('packageUrl')]",
                        "dbType": "None",
                        "connectionString": ""
                    }
                }
            ]
        }
    ],
    "outputs": {}
}
The above code will create the following resources.
1) Storage account
For every function app, we need the storage account. So we should create it before function app. Here the storageSKU is a parameter and it has multiple values. We need to choose any one value from the list while deploying the template based on our function app requirement. 
2) Service Plan
After the storage account, we need a service plan for function app. So this part will create Service plan.
3) Applicaiton Insights
The third resource in the above is the Application insights which is required to store the function app insights
4) Function app
The fourth and final one is the Azure function app and it is the place where all the peices are getting together. 
Here, we need the first 3 resources to be created before creating this function app. So we need to tell the azure as this resource is dependsOn those.
"dependsOn": [
 "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
 "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]",
 "[resourceId('Microsoft.Storage/components', variables('appInsightsName'))]"
]


We can specify the function app settings under properties -> siteConfig -> appSettings as name & value pairs as below

"properties": {
 "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('servicePlanName'))]",
 "siteConfig": {
   "appSettings": [
  {
    "name": "AzureWebJobsDashboard",
    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
  },
  {
    "name": "AzureWebJobsStorage",
    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
  },
  {
    "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
    "value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountName'),'2019-04-01').keys[0].value)]"
  },
  {
    "name": "WEBSITE_CONTENTSHARE",
    "value": "[variables('storageAccountName')]"
  },
  {
    "name": "FUNCTIONS_EXTENSION_VERSION",
    "value": "~2"
  },
  {
    "name": "FUNCTIONS_WORKER_RUNTIME",
    "value": "dotnet"
  },
  {
    "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
    "value": "[reference(resourceId('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
  },
  {
    "name": "dbConnectionString",
    "value": "[parameters('reportingDBConnectionString')]"
  },
  {
    "name": "TestGUID",
    "value": "[parameters('testGuid')]"
  }
   ]
 }
}
The last piece of the process is the sub-resource sourcecontrol inside the FunctionApp. This will define where Azure function code exists. Here I have given packageuri for the function app. 
"resources": [
 {
  "type": "extensions",
  "apiVersion": "2015-08-01",
  "name": "MSDeploy",
  "location": "[resourceGroup().location]",
  "dependsOn": [
   "[resourceId('Microsoft.Web/sites', variables('azureFunctionAppName'))]"
  ],
  "tags": {
   "displayName": "webappdeploy"
  },
  "properties": {
   "packageUri": "[parameters('packageUrl')]",
   "dbType": "None",
   "connectionString": ""
  }
 }
]

Deploying Azure Function

1) Login to the Azure portal
2) Open "Deploy a custom template" (by typing deploy in the search bar)


3) Select "Build your own template in the editor" 
4) Copy the content from Azure ARM template in editor and click on "Save"

5) In the next screen Select the following under Basic settings
Subscription: Azure subscription you want to deploy
Resource Group: Select the existing resource group under which you want to deploy these or Create new 
Location: Select location if you are creating new Resource Group

6) Update the parameters required for the ARM template (Under Settings)  and Accept the Terms and condition by clicking on the checkbox.

7) Click on "Purchase" button. This will deploy the components automatically

Git: Delete commits from a branch

Using Revert feature in Git will keeps the older commit in the branch. It just revert the changes what we have done in a commit. The Revert also will shown as a separate commit in the branch.
But if you want to remove a commit from a branch permanently, you need to use git reset command as below
git reset --hard HEAD~1
Here HEAD~1 means, the commit before the head.
If you want to reset up to a particular commit, use
git reset --hard 
If you already pushed the commits to the repo, you need to use as 
git push origin HEAD --force
This will reset your branch up to the given commit. 
Note: Be careful before doing the above as these will remove the changes in the working directory. Better to keep a backup for the working folder.

Example

The following process will show how it works
1) I have a repo named "test" in my bit bucket. 




It has two branches "master" and "my-branch". "master" branch contains 2 commits and "my-branch" contains 8 commits (along with two master branch commits). 
2) Now I want to reset my-branch to the commit "666f88d" ("fourth.html added") commit. So I just reset my-branch with the command
git reset --hard 666f88d

3) It will reset the changes in your local folder only. Push the changes to repo using
git push origin HEAD --force

4) Now those commits will also be removed from the bitbucket branch


Note: Actually, the commits will not remove from the repo. They just removed from the tree. If you have the direct link to the deleted commit and if you open that link, you can sees the changes you made on that commit