Last active
November 6, 2018 07:34
-
-
Save melvinodsa/20aa11caa814fa7721c0d28c5ca6d2e7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
/** | |
* ConfigService has the configuration utilities required for the application | |
*/ | |
@Injectable({ | |
providedIn: 'root' | |
}) | |
export class ConfigService { | |
/** | |
* api has the url and param configurations required for hitting an api | |
*/ | |
static api: Map<string, APIRequest> = new Map<string, APIRequest>([ | |
['SESSION', { | |
url: '/api/App/Session', | |
params: new Map<string, Param>([]) | |
}], | |
['AUTHURLS', { | |
url: '/api/App/AuthUrls', | |
params: new Map<string, Param>([]) | |
}] | |
]); | |
} | |
//APIRequest is the request to be send for hitting the api | |
export interface APIRequest { | |
//url is the url of the api | |
url: string; | |
//params is the map of the param mapped to a string key. | |
//advantage of this model is that, even if the name of the api param changes | |
//the code business logic in the code need not be changed. | |
//Just the configuration change would be sufficient. | |
params: Map<string, Param>; | |
} | |
/** | |
* Param is the parameter to be sent with a api request | |
*/ | |
export interface Param { | |
//name is the name of the parameter | |
name: string; | |
//value is the value of the parameter | |
value?: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable } from '@angular/core'; | |
import { Observable, throwError, of } from 'rxjs'; | |
import { HttpClient, HttpHeaders } from '@angular/common/http'; | |
import { map, catchError } from 'rxjs/operators'; | |
import { ConfigService, Param, APIRequest } from './config.service'; | |
import { SessionService } from './session.service'; | |
@Injectable({ | |
providedIn: 'root' | |
}) | |
export class HttpService { | |
/** | |
* constructor initializes the http service. It will fetch the session information | |
* when the application boots up. | |
* @param http http client of the angular application | |
* @param session session service of the application | |
*/ | |
constructor(private http: HttpClient, private session: SessionService) { | |
/* | |
* We will check whether the session exists. | |
* If session exists we will store the session's access token in the application variable | |
* Else we will set the session service's access token as empty | |
*/ | |
this.get({ | |
hash: 'SESSION' | |
}).subscribe(resp => { | |
session.setAuthToken(resp.AccessToken); | |
}, error => { | |
console.log(error); | |
session.setAuthToken(''); | |
}) | |
} | |
/** | |
* get is GET request for hitting resources/api | |
* | |
* @param {Request} req the request configuration required for hitting the api/resource | |
*/ | |
get(req: Request): Observable<any> { | |
/* | |
* We will find whether api do exist for the given hash of the request argument | |
* We will set the parameters of the request | |
* We will set the header for auth if available | |
* We will set the loader if avalilable to loading state | |
* Then we will return the get request of http client piped with a transformation function. | |
*/ | |
//checking whether the api exists with the hash of the request | |
let api = ConfigService.api.get(req.hash); | |
if(!api) { | |
return throwError('Could not find the api hash'); | |
} | |
//variables for storing the request options | |
let params = this.getParams(req, api); | |
let options = { headers: new HttpHeaders()}; | |
//joining the params | |
options['params'] = params.map(p => p.name+'='+p.value).join('&'); | |
//setting the headers | |
options.headers.set('Content-Type', 'application/x-www-form-urlencoded'); | |
//if session availanble will set the same | |
if(this.session.getAuthToken().length > 0) { | |
options.headers.set(SessionService.AuthHeader, this.session.getAuthToken()); | |
} | |
//setting the loader of the request | |
if(req.loader) { | |
req.loader.l[req.loader.p] = false; | |
} | |
return this.http.get(api.url, options).pipe( | |
catchError(handleError({})), | |
map(postApi.bind(req)) | |
); | |
} | |
/** | |
* | |
* @param {Request} req request from which the parameter values has to be taken. | |
* @param {APIRequest} api apirequest which ahs the parameter names of the api and the url | |
*/ | |
getParams(req: Request, api: APIRequest) :Param[] { | |
/* | |
* We will proceed only if there exist params | |
* We will iterate through the params and add its value based on the parameter name. | |
*/ | |
let params: Param[] = []; | |
//setting the parameters | |
if(!req.params) { | |
return params; | |
} | |
//we will iterate through the params given by the request and will find the corresponding params in the api | |
let it = req.params.entries(); | |
let param = it.next(); | |
while(param) { | |
//we have a valid parameter key. we will try to get it from the api parameters map | |
let paramKey = param.value[0]; | |
let apiPK = api.params.get(paramKey); | |
if(!apiPK) { | |
//If couldn't find the api key in the api params, skip it | |
continue; | |
} | |
//we also have api param key . now add it to the params | |
params.push({name: apiPK.name, value: param.value[1]}); | |
param = it.next(); | |
} | |
return params; | |
} | |
} | |
/** | |
* Request is the interface to be implemented by an object | |
* to be a request for the HttpService | |
*/ | |
export interface Request { | |
//hash is the hash with which the request is declared in the config service | |
hash: string; | |
//params are the parameters to be passed on with the web request | |
params?: Map<string, string>; | |
//loader to be used while making the api request | |
loader?: Loader; | |
} | |
/** | |
* Loader has to be implemented by any object that represent a loader. | |
* The p property of the l object will be set true by the http service. | |
*/ | |
export interface Loader { | |
l: any; //l is the loader object | |
p: string; //p is the property of the loader to be set true after loading | |
} | |
/** | |
* postApi intercepts the response from the get/post request. | |
* It will do the session check and will disable the loader | |
* @param res respones after the apio hit | |
*/ | |
function postApi(res: any): any { | |
/* | |
* Will disable the loader | |
* Will check whether the session is disabled or not. If so will inform the session service about the same. | |
*/ | |
if(this.loader){ | |
this.loader.l[this.loader.p] = true; | |
} | |
if(res.SessionExpired) { | |
this.session.setAuthToken(''); | |
} | |
return res; | |
} | |
/** | |
* handleError handles any error happening in the api hit | |
* @param result result is the result from the api | |
*/ | |
function handleError<T> (result?: T) { | |
return (error: any): Observable<T> => { | |
console.error(error); // log to console instead | |
// Let the app keep running by returning an empty result. | |
return of(result as T); | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NgModule } from '@angular/core'; | |
import { Routes, RouterModule } from '@angular/router'; | |
import { LoginComponent } from './login/login.component'; | |
import { PagesComponent } from './pages.component'; | |
import { HomeComponent } from './home/home.component'; | |
/** | |
* Routes has all the routes to the pages component. | |
*/ | |
const routes: Routes = [ | |
{ path: '', component: PagesComponent, | |
children: [ | |
{ path: 'home', component: HomeComponent }, | |
{ path: 'login', component: LoginComponent }, | |
{ path: '', redirectTo: 'home', pathMatch: 'full' }, | |
] | |
} | |
]; | |
/** | |
* This module implements the routing of the pages module. | |
*/ | |
@NgModule({ | |
imports: [RouterModule.forChild(routes)], | |
exports: [RouterModule] | |
}) | |
export class PagesRoutingModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, OnInit, OnDestroy } from '@angular/core'; | |
import { Router } from '@angular/router'; | |
import { Subscription } from 'rxjs'; | |
import { HttpService, SessionService } from '../core/services'; | |
import { DarkTheme } from '../theme/theme'; | |
/** | |
* PagesComponent bootstraps the pages in the application. | |
* The session check is done at this level. | |
*/ | |
@Component({ | |
selector: 'brain-pages', | |
templateUrl: './pages.component.html', | |
styleUrls: ['./pages.component.scss'] | |
}) | |
export class PagesComponent implements OnInit, OnDestroy{ | |
/** | |
* sessIns is the session instance of the application. | |
* This is subscribed to the session changes of the session service | |
* If when session become false, it will load the login page | |
*/ | |
sessIns: Subscription; | |
/** | |
* sessionEnabled flag indicate whether the session is enabled or not. | |
*/ | |
sessionEnabled: boolean; | |
/** | |
* themes is the object with keys as the page names mapped to the theme to applied to the page. | |
*/ | |
themes= { | |
HEADER: new DarkTheme() | |
} | |
/** | |
* constructor of the pages component. It do require the following services to be inited before this component. | |
* @param http http service of the application | |
* @param session session service of the application | |
* @param router router of the angular | |
*/ | |
constructor(private http: HttpService, private session: SessionService, private router: Router) {} | |
/** | |
* We will check whether the session is enabled or not. if not enabled will logout. | |
* We will basically subscribe to the session service for the same. So that any changes can dynamically can addressed. | |
*/ | |
ngOnInit() { | |
this.sessIns = this.session.session().subscribe(isEnabled => { | |
this.sessionEnabled = isEnabled; | |
if(!this.sessionEnabled) { | |
this.router.navigate(['pages', 'login']); | |
} | |
}) | |
} | |
/** | |
* We will unsubscribe to the session service | |
*/ | |
ngOnDestroy() { | |
this.sessIns.unsubscribe(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Injectable, EventEmitter } from '@angular/core'; | |
import { Observable } from 'rxjs'; | |
/** | |
* SessionService provides the utilities for proving the session accross the application | |
*/ | |
@Injectable({ | |
providedIn: 'root' | |
}) | |
export class SessionService { | |
/** | |
* AuthHeader is the header to be used for setting the auth token while hitting the api | |
*/ | |
static AuthHeader = 'auth-header'; | |
/** | |
* sessionSet is flag indicating whether the session for the application is set or not | |
*/ | |
private sessionSet = false; | |
/** | |
* sessionEmit emits the session information to the subscribers | |
*/ | |
private sessionEmit: EventEmitter<boolean> = new EventEmitter<boolean>(); | |
/** | |
* authToken is the authentication token to be used for accessing the api | |
*/ | |
private authToken: string = ''; | |
/** | |
* getAuthToken returns the authentication token available in the application | |
*/ | |
getAuthToken(): string { | |
return this.authToken; | |
} | |
/** | |
* setAuthToken sets the new auth token to the session | |
* | |
* @param {string} token is the auth token to be set | |
*/ | |
setAuthToken(token: string) { | |
/* | |
* We will set the session set flag as true | |
* Then we will set the auth token | |
* And then emits the session emit to let the subscribers know that session has been changed | |
*/ | |
this.sessionSet = true; | |
this.authToken = token; | |
this.sessionEmit.emit((this.authToken && this.authToken.length > 0?true: false)) | |
} | |
/** | |
* session returns an observable stating whether the session exists or not. | |
* This function will wait for the first time to get the session set and let | |
* it's subscriber known whenever the session information changes. | |
*/ | |
session() : Observable<boolean> { | |
/* | |
* If the session is already not set we will create an observable that emits the session when | |
* the sessionEmit event emitter emits the session information change | |
* If the session is already set we will create an observable that emits the session when | |
* the sessionemit event emitter emits the session information change. Also will emit the current session information. | |
*/ | |
//when the session is not set simply create the observable that is subscribed to the session event emiter | |
if(!this.sessionSet) { | |
let ob = Observable.create((observer) => { | |
this.sessionEmit.subscribe((sess: boolean) => { | |
observer.next(sess); | |
}); | |
}); | |
return ob; | |
} | |
//if the session is already set create the observale that by default emits the session information along with the subscription to the vent emiter; | |
let ob = Observable.create((observer) => { | |
observer.next((this.authToken && this.authToken.length > 0?true: false)); | |
this.sessionEmit.subscribe((sess: boolean) => { | |
observer.next(sess); | |
}); | |
}); | |
return ob; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment