6 Key Merging Areas in Consulting

posted Mar 4, 2017, 7:37 AM by Julian Zhu   [ updated Mar 4, 2017, 7:37 AM ]




Angular 2: Implement Pagination UI Component

posted Dec 25, 2016, 9:47 AM by Julian Zhu   [ updated Jan 21, 2017, 6:27 AM ]

Demo: 


GitHub (with just basic source code for this UI Pagination Component)


Background

  • I need a simple (yes, simple is beautiful!) pagination UI component for my Angular 2 project. 
  • I tried couple of open source ones from the Internet, but it is either too complicated or not working at all. 

I decided to implement my own pagination UI component. 

Design/Implementation Principle: Easy to Understand & Use, Flexible to Customize, and open source. 

UI Example: How does it look? 

The styling is using Bootstrap default css - nothing special. By default, it uses bootstrap from the Angular 2 application level. 
Of course, you could customize it easily by defining your own style in the css file. 





See how the pagination scale changes accordingly. 

Key Features:

  • With the buttons liking to the very first, very last page. 
  • Always showing the first page [1], and the last page [whatever the total - 1] 
  • Always showing the current page, and surrounding pages
  • Hide page indices that are too far away from the current page, and only show the "1"s, such as 21, 31, 41, ... and so on. 
Of course, you can easily customize the logic above. 

Now Let's dive into the Angular 2 Code: 

We create a component folder name "ui" for our pagination component implementation: 

[ui/] 
- pagination.component.ts
- pagination.component.html
- pagination.component.css


We will ignore .css for now - you could define and apply any css style you want. 

Let's focus on the two core implementation files: 

pagination.component.ts

 
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

// Menu Component
@Component({
  moduleId: module.id,
  selector: "my-pagination",
  templateUrl: 'pagination.component.html',
  styleUrls: [ 'pagination.component.css' ]
})

export class PaginationComponent implements OnInit {

  @Input() total: number = 0;   // total pages
  @Input() page: number = 0;    // current/selected page
  @Input() size: number = 0;    // # of elements per page
  @Input() keyword: string = '';    // search data keyword

  @Output()
  pageChanged: EventEmitter<{keyword: string, page: number, size: number}> = new EventEmitter();

  pages: Array<any> = new Array();

  constructor() { }

  ngOnInit(): void {
    this.pages = new Array(this.total);
    for(let pg = 0; pg < this.total; pg ++) {
      this.pages.push({page: pg, current: (pg == this.page)});
    }
  }

  // when user select a page
 // We emit an event and notify the parent component to handle refresh/loading data
  selectPage(page: number, event?:MouseEvent) {
      if (event) {
          event.preventDefault();
          //event.defaultPrevented;
          this.page = page;
        }
    this.pageChanged.emit({keyword: this.keyword, page: this.page, size: this.size});
  }

  // the paging logic
 // this is used to determine how to display (or whether or not to display) page index/link. 
  showPageLink(page: number):boolean {
    if(page ==0 || page == this.total -1) return true;
    if(Math.abs(page - this.page) > 5) {
      if(page%10 == 0) return true;
      else return false;
    } else {
      return true;
    }
  }

}


pagination.component.html

 
<ul *ngIf="total > 0" class="pagination  pagination-sm" >
    <li class="pagination-first page-item"
          [class.disabled]="page == 0">
      <a class="page-link" href (click)="selectPage(0, $event)"><span class="glyphicon glyphicon-step-backward" aria-hidden=true></span></a>
    </li>

    <li *ngIf="page > 0" class="pagination-prev page-item"
          [class.disabled]="page == 0">
      <a class="page-link" href (click)="selectPage(page - 1, $event)"><span class="glyphicon glyphicon-chevron-left" aria-hidden=true></span></a>
    </li>

    <li *ngFor="let pg of pages" class="pagination-page page-item" [class.active]="pg != null && page == pg.page">
      <a *ngIf="pg && showPageLink(pg.page)" class="page-link" href (click)="selectPage(pg.page, $event)">{{ pg.page + 1 }}</a>
    </li>

    <li *ngIf="page < (total - 1)" class="pagination-next page-item"
          [class.disabled]="page == (total - 1)">
      <a class="page-link" href (click)="selectPage(page + 1, $event)"><span class="glyphicon glyphicon-chevron-right" aria-hidden=true></span></a></li>

    <li class="pagination-last page-item"
          [class.disabled]="page == (total - 1)">
      <a class="page-link" href (click)="selectPage(total - 1, $event)"><span class="glyphicon glyphicon-step-forward" aria-hidden=true></span></a></li>
  </ul>






The following is not the core implementation of this UI component. 
This is to illustrate how to use it. 

Now, let's use it: 

My example is to load a list of Twitter messages (via HTTP API call) and populate the page (with pagination component). 

Note: 
The logic for loading tweets is not significant -- some components may be missing in this blog. 
I don't intend to show you how to implement a data API service with a business object component (in this case, Twitter messages). 
Please focus on the use of Pagination component. 

If you need help with building Angular 2 data components with HTTP API services, please write to me separately. 

tweets.service.ts

 
import { Injectable,EventEmitter } from '@angular/core';
import { Headers, Http, Response } from '@angular/http';

import { Config, Text } from '../common/config';
import { APIRequest } from '../common/api-request';
import { APIResponse } from '../common/api-response';
import { Event } from '../common/event';

import { Tweet } from './tweet';

import 'rxjs/add/operator/toPromise';

@Injectable()
export class TweetService {

  message: string;
  keyword: string;
  statusChange: EventEmitter<({object:any, message:string})> = new EventEmitter();

  private headers = new Headers({'Content-Type': 'application/json'});

  constructor(private http: Http) {}

  // TODO: 
  getObjects(sender: string, page: number, size: number): Promise<APIResponse> {

      this.sender = sender;
      let apiRequest = <APIRequest>({
          apiKey: 'example_api_key',
          operator: '',
          token: 'example_token',
          page: page,
          size: size,
          keyword: keyword
      });

      const url = 'http://************/tweets'; // define your own API end point

      return this.http.post(url, JSON.stringify(apiRequest), {headers: this.headers})
      .toPromise()
      .then(
          response =>
          {
          console.log(response);
            if(response.json().code == '200') {
              return response.json() as APIResponse
            } else {
              this.message = Text.val(500);
              this.emitStatusChangeEvent(null, this.message);
            }
          }
        )
      .catch((ex) => this.handleError(ex));

  }

  private handleError(error: any) {
    this.emitStatusChangeEvent(null, Text.val(500));
    //return Promise.reject(error.message || error);
  }

  emitStatusChangeEvent(object: any, message: string) {
    this.statusChange.emit({object:object, message:message});
  }

  getStatusChangeEmitter() {
    return this.statusChange;
  }
}



tweets.component.ts


import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { ActivatedRoute, Params }   from '@angular/router';

import { APIResponse } from '../common/api-response';
import { Event } from '../common/event';

import { Tweet } from './tweet';
import { TweetDetailComponent } from './tweet-detail.component';

import { TweetService } from './tweet.service';

@Component({
    moduleId: module.id,
    selector: 'my-tweets',
    templateUrl: 'tweets.component.html',
    styleUrls: [ 'tweets.component.css' ]
})

export class TweetsComponent implements OnInit {

  objects: Tweet[];
  selectedObject: Tweet;
  message: string;
  subscription: any;

  apiResponse: APIResponse;
  sender: string;
  page: number;
  size: number;

  constructor(
    private router: Router,
    private activatedRoute: ActivatedRoute,
    private service: TweetService,
    private authGuard: AuthGuard
  ) {
    this.message = this.service.message;
    this.subscription = this.service.getStatusChangeEmitter()
    .subscribe(($event:any) => {
      if($event.object instanceof Event && $event.object.type == Event.RELOAD) {
        // do nothing - reserved for future
        this.ngOnInit();
      }
      this.message = $event.message;
    } );
  }

  getObjects(sender: string, page: number, size: number): void {

    this.sender = sender;
    this.service.getObjects(sender, page, size).then(
      apiResponse => {
        this.apiResponse = apiResponse;
        this.objects = apiResponse.body as Tweet[];
      }
    );
  }

  ngOnInit(): void {
    this.activatedRoute.params.forEach((params: Params) => {
      let sender = params['sender'];
      this.getObjects(sender, 0, 50);
    })
  }
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }

  onSelect(object: Tweet): void {
    this.selectedObject = object;
  }

  selectSender(object: Tweet): void {
    if(object != null) {
      this.sender = object.sender;
      this.router.navigate(['/tweets', object.sender]);
    } else {
      this.sender = '';
      this.router.navigate(['/tweets']);
    }
  }

 // this responds to user selection of new page and load/refresh data
  selectObjects(event: any) {
    this.getObjects(event.keyword, event.page, event.size);
  }
}






tweets.component.html


<div class="container">

<div *ngIf="message" class="alert alert-warning">
  {{ message }}
</div>

<div class="panel panel-default">

  <div *ngIf="apiResponse">
    <div align="center">
      <my-pagination [keyword]="sender" [page]="0" [size]="50" [total]="apiResponse.totalPages" (pageChanged)="selectObjects($event)"></my-pagination>
    </div>

Total Records: <span class="badge">{{ apiResponse.totalElements }}</span>
  </div>
  <table class="table table-bordered table-striped table-hover table-condensed">

    <tr>
      <th>Sender</th>
     <th>Time</th>
      <th>Text</th>
    </tr>

    <tr *ngFor="let object of objects"
      [class.selected]="object == selectedObject"
      (click)="onSelect(object)">
      <td>
        <button class="btn btn-primary btn-sm" (click)="selectSender(object)"><span class="glyphicon glyphicon-send" aria-hidden=true></span> {{object.sender}}</button>
      </td>
      <td>
        <h6>{{object.time | date:'MM/dd/yyyy hh:mm a' }}</h6>
      </td>
      <td>
        {{object.text}}
      </td>
    </tr>
  </table>

</div>

</div>




Enjoy!!!

About The Author

Julian Zhu is a principal consultant and managing partner at OSC Technologies  at the Great Boston area. He previously worked at CVS Health managing Enterprise Architecture team, and consultant at Greenwich Technology Partners. Contact him at julian.zhu@oscgc.com if you have any question. Thank you. 




Bring my Pure Pro to the Cloud

posted Dec 22, 2016, 2:55 PM by Julian Zhu   [ updated Dec 22, 2016, 2:55 PM ]

Why is this exciting (at least to me)? 
  • Extended Pure Pro (Mobile App) to the Cloud
  • Enabled API for data capturing and integration
  • Social Platform Integration
  • And Coming Soon: 
    • Deep Learning ...



Coming Soon: new JulianZhu.us Site

posted Dec 8, 2016, 8:09 AM by Julian Zhu   [ updated Dec 8, 2016, 6:18 PM by Julian Zhu ]

Play Hard?

posted Aug 3, 2016, 8:35 AM by Julian Zhu   [ updated Dec 27, 2016, 7:07 AM ]

Update: 

I never completed the Play framework since I don't believe Play is so practical to learn when business problems can be resolved by many other technologies that I am very familiar with. Why bother to Play? :-)

Of course, you don't have to agree with me. Everyone has his/her own background/experience, and perspective. 


(DRAFT) ... Coming soon ...

Issues: 

- double quote for SBT_...."
- Create a blank sbtconfig.txt in conf folder

add mysql connector dependencies to build.sbt
"mysql" % "mysql-connector-java" % "5.1.29",

enable DB
http://www.waitingforcode.com/play-framework/database-and-jpa-in-play-framework/read
http://www.techferry.com/articles/hibernate-jpa-annotations.html#GeneratedValue


Basic Steps:

Install Play

- Install JDK 1.8
- download Play 


set PATH=C:\jdk1.8.0_65\bin;C:\activator-1.3.10\bin;C:\Windows\System32

Running into issues: 

  • findstr cannot be found ==> add C:\Windows\System32 to the PATH
  • create an empty config file in conf\ folder: sbtconfig.txt
  • C:\RUNNING_PID not found ==> Run command as Admin: activator ui 

To Start Activator UI

C:\> activator ui



Create a new Application

C:\> activator new my-first-app play-java




Configure Database in Play

file: conf\application.conf

default.driver = com.mysql.jdbc.Driver
default.url = "jdbc:mysql://localhost/my_play"
default.username = your_db_user
default.password = your_db_password
default.jndiName=MyPlayDS

file: build.sbt: 
In order to get JDBC driver library, you will need to include it (and hibernate JPA if you plan to use it) in build.sbt script: 

libraryDependencies ++= Seq(
javaJdbc,
"mysql" % "mysql-connector-java" % "5.1.36",
javaJpa,
"org.hibernate" % "hibernate-entitymanager" % "5.1.0.Final",
cache,
javaWs
)



CREATE DATABASE IF NOT EXISTS play_store CHARACTER SET utf8 COLLATE utf8_general_ci; USE 'play_store'; CREATE TABLE IF NOT EXISTS categories ( id INT(3) UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE IF NOT EXISTS products ( id INT(5) UNSIGNED NOT NULL AUTO_INCREMENT, categories_id INT(3) UNSIGNED NOT NULL, name VARCHAR(100) NOT NULL, description TEXT NOT NULL, price DOUBLE(7, 2) NOT NULL, PRIMARY KEY (id) );



file: conf/applicaiton.conf
jpa.default=MyPlayPersistenceUnit

Create a new file for persistence configuration: conf/META-INF/persistence.xml

<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">

<persistence-unit name="MyPlayPersistenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<non-jta-data-source>MyPlayDS</non-jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
</properties>
</persistence-unit>

</persistence>


Define JPA Entities (Using Hibernate)


@Entity
@Table (name="country")

public class Country {

    private int id;

    private String name; 

    private List<Country> countries;

    private List<City> cities; 

    @Id
    @GeneratedValue(strategy= IDENTITY) 
    @Column(name="id")
    public int getId() {
        return this.id; 
    }

    @Column(name="name") 
    public String getName() { 
        return this.name; 
    }

    @OneToMany(mappedBy="country") 
    public List<City> getCities() {
        return this.cities;
    }

    public void setId(int id) { 
        this.id = id; 
    }

    public void setName(String name) { 
        this.name = name; 
    }

    public void setCities(List cities) {
        this.cities = cities;
    }

    @Override
    public String toString() { 
        return "Country {id: " + this.id + ", name: " + this.name + "}";
    }

}

Define Service Layer

import play.db.jpa.JPA;

public class CountryService {

    public Country getById(int id) { 
        return JPA.em().find(Country.class, id); 
    }

}



Microsoft Azure Cloud for Enterprise Application Security

posted May 30, 2016, 5:55 PM by Julian Zhu   [ updated Jul 23, 2016, 5:43 AM ]

Summary

A large enterprise needs to consider how to solve user identity management and application security access control problems while moving to the cloud. Microsoft Azure offers more than Cloud infrastructure. Comparing with other cloud platform providers, Microsoft Azure is very unique in the way how Microsoft extends its enterprise Activity Directory services to the cloud: 

  • Active Directory on the Cloud (Azure AD)
  • B2C Directory Service
  • Access Control Services or Access Management (IAM)
  • Identity Management (IDM)
  • APIs
  • Office 365 Integration

OSC and Microsoft Joint Webinar on "Microsoft Azure and Application Security Integration"


To What the Full Video for the Webinar our team presented on 06/02/2016: 



Overview

Microsoft Azure AD and Access Control Services Connect Right User to the Right Data with Right Access Control






Microsoft Azure Suports OAuth Architecture

Here is a typical oAuth architecture

Microsoft Azure Supports SAML Architecture

Here is a typical SAML architecture

Microsoft Azure AD for B2B/B2C Application Authentication

Here is a high level sequence diagram illustrating architecture.


Demo Application

Our demo app demonstrates the easy application integration with Microsoft Azure for: 
  • Manage Azure Active Directory
  • B2C Application User Enrollment (Sign up) & Login (Authentication) using Azure Acitive Directory
  • Corporate & B2B Application Authentication using Azure Active Directory
  • Enable SSO to Third Party applications (by configuration not by coding) on Azure cloud
  • Enable social identity for authentication (by configuration not by coding) on Azure cloud
Please refer to the webinar video above to see the demo. Or you can always reach out to us request a live demo and/or discussion. 

More Considerations

  • Enterprise Strategy and Road map: Identity Management, Authentication, and SSO
  • AD Synchronization and Migration: Keep AD sync'ed for a hybrid architecture
  • IoT: Support IoT in your enterprise security landscape
  • Other SP and IDP
  • Other Cloud

About The Author

Julian Zhu is the managing partner & principal consultant at OSC Technologies (http://www.oscgc.com) leading technology innovation and consulting. Previously he was Sr. Manager of Enterprise Architecture Team and Enterprise Digital Solution Architect at CVS Health.

Visit Julian's Blog at: http://www.julianzhu.us for more information. To contact Julian for consulting services, please send email julian6866 [at] gmail dot com. 

Implement SAML Single Sign-On Solution

posted May 21, 2016, 7:38 PM by Julian Zhu   [ updated Jun 10, 2016, 9:16 PM ]

This blog provides an over-simplified overview of SAML/SSO implementation. 

SSO/SAML Architecture Overview

Let's review some basics. 

A Single Sign-On Solution typically involves three architecture components: 
  • A User: who wants to access a system (in this case: the resource a Service Provider provides)
  • A Service Provider: manages a business application for a user to access information or functionalities
  • An Identity Provider: an infrastructure component that is responsible for validating user's identity 
The following diagram illustrates the overall work flow on how the SAML/SSO works. 

In the context of SAML/SSO, here is a list of obvious but key architecture info you should understand: 
  • Service Provider and Identity Provider need to know each other in order for the solution to work. 
  • Service Provider will redirect request to Identity Provider (when user has not been authenticated)
  • Identity Provider will post info back to Service Provider (so that Service Provider receives user attribute data to use)
  • The protocol and message format used for the communication: SAML (Security Assertion Markup Language)

SAML/SSO: Context Diagram

Functional Scenario: Implement Identity Provider

There are different scenarios you may design for SSO implementation. 
This tutorial shows how to implement one SAML/SSO scenario: 
  • Identity Provider: We implement an SAML Identity Provider using SimpleSAMLPHP
    • We create a MYSQL database table to store user identity information and attributes
    • We configure SAML settings for this hosted IDP. 
  • Service Provider: We configure Google Security to use our Identity Provider to provide Single Sign-On for Google Mail application. 
Once we implement this, we will able to allow user to input their user name and password (stored in our Identity Provider system) to access Google Mail (enabled to work with our Identity Provider). 

Here it the demo: 

Julian Zhu | OSC Technologies


How to Implement

Step 1. Implement Identity Provider 

(In our example, we use SimpleSAMLPhp. We have a reference implementation using Java as well. )

Implement auth data source (e.g. MySQL database/table as user identity repository)
Download & Install SimpleSAMLphp
Configure SAML2.0-idp-host information (for accessing user identity authsources
Configure SAML2.0-sp-remote information (for Google)

Implement MySQL user identity data store

 Field Name Description
 id Primary key
 user_name user name used for authentication
 password password used for authentication
 uid Could be email address as an attribute mapping to Google account id (email address)

Edit config\authsources.php
This defines what identity data source to use for authenticating user

    'my_google_sso' => array(
        'google_auth:login',
        'dsn => 'mysql:host=127.0.0.1;dbname=my_SSO_database',
        'username' => 'my_sso_db_user',
        'password' => '*&*)[1@39!xxxx',
    ), 

Edit metadata\saml2.0-idp-hosted.php
This tells Identity Provider where to look for authentication source for user identity data. 
    
    'auth' => 'my_google_sso',

Edit metadata\saml2.0-sp-remote.php
This defines service provider meta data - where/how to communicate with the Service Provider. 

$metadata['google.com'] = array(
        'AssertionConsumerService' => 'https://www.google.com/a/you_google_domain.com/acs',
        'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
        'simplesaml.nameidattribute' => 'uid',
        'simplesaml.attributes' => FALSE,
)


OSC Identity Provider - Reference Implementation (Java or PHP)


Step 2. Configure Google (As Service Provider)


Login to google admin console where you can manage the Security settings for Google (as service provider): 




Select "Set up single sign-on (SSO)"


Specify the Identity Provider information: 
This requires information from Identity Provider including a few key URLs for SAML communication, as well as an Identity Provider digital certificate used for data encryption purpose. 



Congratulations

Now you have a complete SAML/SSO solution. 

To extend/customize your own solution, you have choices of implementing technology including Java Open SAML. There are quite a few hosted cloud services that can be worked as Identity Provider to make job easier. However, to select right one for your business may not be that straightforward given IT investment and technology standards  you already have in house. Contact me if you have any question or comment. 

Digital Accessibility: Engage Your Customer More Efficiently

posted May 15, 2016, 7:18 PM by Julian Zhu   [ updated Dec 8, 2016, 8:16 AM by Julian Zhu ]



This blog you are reading now is not about data, it is about "Accessibility".

I will soon write a blog about data and how it supports customer engagement -- my research at college and graduate school was high energy physics and statistics physics -- a lot of modeling and computation. Please stay tuned on that.

Accessibility

To learn basic concept about accessibility, please read my other blog: http://www.julianzhu.us/blog-post/mobileaccessibilityservice

My Research Update

Here is just a quick update on research I recently did and product prototype I have built around "Accessibility".

It also demonstrates key capabilities that native mobile devices can deliver are far more sophisticated and capable than HTML.

This blog is not trying to start another debate about native vs. hybrid vs. HTML. Coming from physics background, I fully understand that hardware and software together can make a perfect digital product. In the real-world application, it totally depends on whether or not you (as business) care or not about those capabilities.

Let me highlight a few things I have built for research and demonstration purpose:
  • Feature 1. (Once User Enables) Mobile Device is now aware of what is happening and will take action on the behalf of user when needed.
  • Feature 2. (Text-to-Speech) Any communication (from inside or outside of mobile device), can now be announced as voice to user. 
  • Feature 3. (Voice Command/Input/Recognition) Mobile App can now prompt and take voice input from user and take action for user.
  • Feature 4. Near-Field-Communication (NFC) devices
  • Feature 5: A powerful mobile app task engine framework
All these features greatly improve user engagement and experience. 

Please note: 

All the features mentioned in this blog have been implemented and ready for integration & customization to enable similar capabilities for your mobile application. 
You don't have to reinvent the wheels. 


Demo 1: Mobile Device Receives an Event, Triggers Automated User Action

In this demo, the action is triggered by mobile push notification to automate user action for the most popular Chinese social app - WeChat. 

YouTube Video


Demo 2: Register a NFC device to Trigger Event/Action on the behalf of the User

This demo shows how the mobile app discovers a new NFC device. Prompt user to register the device for event triggering. 
Once registered, the future scan of the same device will trigger and/or automate action on the behalf of the user - in this demo, it initiates a phone call automatically. 

YouTube Video



Demo 3. Text-To-Speech Triggered When Receiving Social Platform Notifications

Once enabled, mobile app will be aware of the social platform notification events (or any type of events) and convert text to speech (or take any type of actions). 
With this app, you could listen to your favorite social platform messages while driving, and more interestingly, you could use voice to reply (continue reading below). 


Demo 4. Use Voice Command/Input to Respond to an Event - Responding to Social Messages All by Voice

Once enabled by user, mobile app will prompt user for voice command/input. It supports multi-languages. 


Demo 5. Trigger/Automate a Task from Sensor Behaviors

Mobile devices/sensors behavior can be used to trigger events. The following example shows how a rule can be set up by a user for the mobile app to be aware of sensor behavior (in this case monitoring shake behavior) and trigger user defined action - in this case, it automatically initiates a phone call. 

  


Stay Tuned for More on this Topic: 

  • Security for Implementing Accessibility

About The Author

Julian Zhu is the managing partner & principal consultant at OSC Technologies (http://www.oscgc.com) leading technology innovation and consulting. Previously he was Sr. Manager of Enterprise Architecture Team and Enterprise Digital Solution Architect at CVS Health.

Visit Julian's Blog at: http://www.julianzhu.us for more information. To contact Julian for consulting services, please send email julian6866 [at] gmail dot com. 

Build a RESTFul Web Service/API Using Google App Scripts in 5 Minutes

posted May 8, 2016, 3:07 PM by Julian Zhu   [ updated May 17, 2016, 6:21 PM by Julian Zhu ]

There are many ways of building RESTful web Sevices & API. 

This tutorial shows you how to build one in an easy and quick way using Google Sites and App Scripts to serve API and use Google Sheets to serve data. 

Typical use is to serve public data services/API. 

Google oAuth 2.0 can be easily added without any coding. 

Step 1. Prepare Your Data


Create a Google Sheet in Google Drive and popular data. The highlighted part is the Google Sheet ID that you will use later to reference this sheet and retrieve data. 



Step 2. Create a Google Site (https://sites.google.com)

If you don't know how to create a Google Site, you may read some other quick guide on the Internet. 


Step 3. Go to Google Site Edit Mode


From the drop down, select "Manage site". 


Step 4. Click "Apps Scripts" 




Step 5. Create a new Google Apps Script by clicking "Add new script". 

Your existing scripts are listed here so that you can always come back and edit them. 


Step 6. Now, you can start writing your first Google Apps Script. 


If you know JavaScript, coding is very straightforward. 


Step 7. Let's Copy & Paste my sample code

Company Ticker API
 
/*** main HTTP (GET and POST) interaction *************************/

function doGet(e) {
  return doPost(e);  
}

function doPost(e) {
 
  var apiKey = e.parameters.apiKey; 
  var operation = e.parameters.operation;
  var args = e.parameters.args; 
  var response;
  
  if(!validateKey(apiKey)) {
    response = sendFailResponse("500", "Fail - Invalid API Key");
  } else {
    if(operation == 'GetTickers') {
      response = GetTickers(); 
    } else {
      response = sendFailResponse("500", "Fail - operation not supported");
    }
  }
  return ContentService.createTextOutput(JSON.stringify(response))
    .setMimeType(ContentService.MimeType.JSON);
}

/********************* API Services ***********************************/
// Validate API Key
function validateKey(apiKey) {
  return (apiKey == '123456');
}

// Send failure response with code/message
function sendFailResponse(code, message) { 
  return {
      status: {
        code: code,
        message: message
      },
      payload: {
      }      
    };  
}

function GetTickers() {
  return {
    status: {
      code: "200",
      message: "Success"
    },
    payload: loadTickets()
  };
}


/********************** SHEET Transactions *****************************/
function getMasterSheetID() {
  return "34Vp54TdfVnO547R3N843olkrzJm2Vtza4JZIw6h8Wu33D"; // this is the sheet we created earlier
}

function loadTickets() {
  var objs = new Array(); 
  var spreadSheet = SpreadsheetApp.openById(getMasterSheetID()); 
  var sheet = spreadSheet.getActiveSheet(); 
  var data = sheet.getDataRange().getValues(); 
  // start from row 1
  for(var i=1; i < data.length; i++) {
    var obj = {
      ticker: data[i][0], 
      name: data[i][1]
    };
    objs.push(obj);
  }
  return objs;
}





Step 8. Save the Scripts. 


You may click "Run" and select methods to test run. 
Some method (e.g. doPost()) may depend on request parameters, and it may report error -- don't worry. We will test the API soon. 

Once we are ready to deploy and test, we may click "Publish" for testing. See next step on "Publish". 


Step 9. Publish API



Select appropriate permission to publish API script. In order for the API being used publicly, you may follow the similar permission on the example below. 


Follow the screen to authorize the publishing. 



Step 10. Congratulations! You just published a RESTFul API. 



To test the API: Copy the API URL from above and append it with a valid operation parameter: 

https//script.google.com/macros/s/AKfy.........?apiKey=123456&operation=GetTickers

Tip: Please use HTTP Post in your API client code. 

If you don't append appropriate API key or operation, you will get an failed response message. 

Enjoy!

About The Author

Julian Zhu is a principal consultant and managing partner at OSC Technologies  at the Great Boston area. He previously worked at CVS Health managing Enterprise Architecture team, and consultant at Greenwich Technology Partners. Contact him at julian.zhu@oscgc.com if you have any question. Thank you. 

D3.JS and Powerball

posted Jan 19, 2016, 2:43 PM by Julian Zhu   [ updated May 17, 2016, 6:22 PM by Julian Zhu ]

d3.js demo by Julian. More: d3js.org

Please play responsibly. Enjoy! 




1-10 of 29