Skip to content

Commit

Permalink
Updated to new HttpClient
Browse files Browse the repository at this point in the history
  • Loading branch information
cornflourblue committed May 31, 2018
1 parent 71d8b15 commit f071ba4
Show file tree
Hide file tree
Showing 12 changed files with 1,022 additions and 1,955 deletions.
6 changes: 3 additions & 3 deletions app/_guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

@Injectable()
export class AuthGuard implements CanActivate {

constructor(private router: Router) { }

canActivate() {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (localStorage.getItem('currentUser')) {
// logged in so return true
return true;
}

// not logged in so redirect to login page
this.router.navigate(['/login']);
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});
return false;
}
}
88 changes: 46 additions & 42 deletions app/_helpers/fake-backend.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,61 @@
import { Http, BaseRequestOptions, Response, ResponseOptions, RequestMethod } from '@angular/http';
import { MockBackend, MockConnection } from '@angular/http/testing';

export function fakeBackendFactory(backend: MockBackend, options: BaseRequestOptions) {
// configure fake backend
backend.connections.subscribe((connection: MockConnection) => {
let testUser = { username: 'test', password: 'test', firstName: 'Test', lastName: 'User' };

// wrap in timeout to simulate server api call
setTimeout(() => {

// fake authenticate api end point
if (connection.request.url.endsWith('/api/authenticate') && connection.request.method === RequestMethod.Post) {
// get parameters from post request
let params = JSON.parse(connection.request.getBody());

// check user credentials and return fake jwt token if valid
if (params.username === testUser.username && params.password === testUser.password) {
connection.mockRespond(new Response(
new ResponseOptions({ status: 200, body: { token: 'fake-jwt-token' } })
));
import { Injectable } from '@angular/core';
import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/delay';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/materialize';
import 'rxjs/add/operator/dematerialize';

@Injectable()
export class FakeBackendInterceptor implements HttpInterceptor {

constructor() { }

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let testUser = { id: 1, username: 'test', password: 'test', firstName: 'Test', lastName: 'User' };

// wrap in delayed observable to simulate server api call
return Observable.of(null).mergeMap(() => {

// authenticate
if (request.url.endsWith('/api/authenticate') && request.method === 'POST') {
if (request.body.username === testUser.username && request.body.password === testUser.password) {
// if login details are valid return 200 OK with a fake jwt token
return Observable.of(new HttpResponse({ status: 200, body: { token: 'fake-jwt-token' } }));
} else {
connection.mockRespond(new Response(
new ResponseOptions({ status: 200 })
));
// else return 400 bad request
return Observable.throw('Username or password is incorrect');
}
}

// fake users api end point
if (connection.request.url.endsWith('/api/users') && connection.request.method === RequestMethod.Get) {
// check for fake auth token in header and return test users if valid, this security is implemented server side
// in a real application
if (connection.request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
connection.mockRespond(new Response(
new ResponseOptions({ status: 200, body: [testUser] })
));
// get users
if (request.url.endsWith('/api/users') && request.method === 'GET') {
// check for fake auth token in header and return users if valid, this security is implemented server side in a real application
if (request.headers.get('Authorization') === 'Bearer fake-jwt-token') {
return Observable.of(new HttpResponse({ status: 200, body: [testUser] }));
} else {
// return 401 not authorised if token is null or invalid
connection.mockRespond(new Response(
new ResponseOptions({ status: 401 })
));
return Observable.throw('Unauthorised');
}
}

}, 500);
// pass through any requests not handled above
return next.handle(request);

})

});

return new Http(backend, options);
// call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
.materialize()
.delay(500)
.dematerialize();
}
}

export let fakeBackendProvider = {
// use fake backend in place of Http service for backend-less development
provide: Http,
useFactory: fakeBackendFactory,
deps: [MockBackend, BaseRequestOptions]
provide: HTTP_INTERCEPTORS,
useClass: FakeBackendInterceptor,
multi: true
};
3 changes: 2 additions & 1 deletion app/_helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './fake-backend';
export * from './jwt.interceptor';
export * from './fake-backend';
20 changes: 20 additions & 0 deletions app/_helpers/jwt.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (currentUser && currentUser.token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}

return next.handle(request);
}
}
41 changes: 13 additions & 28 deletions app/_services/authentication.service.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,27 @@
import { Injectable } from '@angular/core';
import { Http, Headers, Response } from '@angular/http';
import { Observable } from 'rxjs';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map'

@Injectable()
export class AuthenticationService {
public token: string;
constructor(private http: HttpClient) { }

constructor(private http: Http) {
// set token if saved in local storage
var currentUser = JSON.parse(localStorage.getItem('currentUser'));
this.token = currentUser && currentUser.token;
}

login(username: string, password: string): Observable<boolean> {
return this.http.post('/api/authenticate', JSON.stringify({ username: username, password: password }))
.map((response: Response) => {
login(username: string, password: string) {
return this.http.post<any>('/api/authenticate', { username: username, password: password })
.map(user => {
// login successful if there's a jwt token in the response
let token = response.json() && response.json().token;
if (token) {
// set token property
this.token = token;

// store username and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify({ username: username, token: token }));

// return true to indicate successful login
return true;
} else {
// return false to indicate failed login
return false;
if (user && user.token) {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify(user));
}

return user;
});
}

logout(): void {
// clear token remove user from local storage to log user out
this.token = null;
logout() {
// remove user from local storage to log user out
localStorage.removeItem('currentUser');
}
}
20 changes: 4 additions & 16 deletions app/_services/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions, Response } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map'
import { HttpClient } from '@angular/common/http';

import { AuthenticationService } from './index';
import { User } from '../_models/index';

@Injectable()
export class UserService {
constructor(
private http: Http,
private authenticationService: AuthenticationService) {
}

getUsers(): Observable<User[]> {
// add authorization header with jwt token
let headers = new Headers({ 'Authorization': 'Bearer ' + this.authenticationService.token });
let options = new RequestOptions({ headers: headers });
constructor(private http: HttpClient) { }

// get users from api
return this.http.get('/api/users', options)
.map((response: Response) => response.json());
getAll() {
return this.http.get<User[]>('/api/users');
}
}
18 changes: 10 additions & 8 deletions app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

// used to create fake backend
import { fakeBackendProvider } from './_helpers/index';
import { MockBackend, MockConnection } from '@angular/http/testing';
import { BaseRequestOptions } from '@angular/http';

import { AppComponent } from './app.component';
import { routing } from './app.routing';

import { AuthGuard } from './_guards/index';
import { JwtInterceptor } from './_helpers/index';
import { AuthenticationService, UserService } from './_services/index';
import { LoginComponent } from './login/index';
import { HomeComponent } from './home/index';
Expand All @@ -20,7 +19,7 @@ import { HomeComponent } from './home/index';
imports: [
BrowserModule,
FormsModule,
HttpModule,
HttpClientModule,
routing
],
declarations: [
Expand All @@ -32,11 +31,14 @@ import { HomeComponent } from './home/index';
AuthGuard,
AuthenticationService,
UserService,

{
provide: HTTP_INTERCEPTORS,
useClass: JwtInterceptor,
multi: true
},

// providers used to create fake backend
fakeBackendProvider,
MockBackend,
BaseRequestOptions
fakeBackendProvider
],
bootstrap: [AppComponent]
})
Expand Down
2 changes: 1 addition & 1 deletion app/home/home.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class HomeComponent implements OnInit {

ngOnInit() {
// get users from secure api end point
this.userService.getUsers()
this.userService.getAll()
.subscribe(users => {
this.users = users;
});
Expand Down
23 changes: 15 additions & 8 deletions app/login/login.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Router, ActivatedRoute } from '@angular/router';
import 'rxjs/add/operator/first';

import { AuthenticationService } from '../_services/index';

Expand All @@ -11,27 +12,33 @@ import { AuthenticationService } from '../_services/index';
export class LoginComponent implements OnInit {
model: any = {};
loading = false;
returnUrl: string;
error = '';

constructor(
private route: ActivatedRoute,
private router: Router,
private authenticationService: AuthenticationService) { }

ngOnInit() {
// reset login status
this.authenticationService.logout();

// get return url from route parameters or default to '/'
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
}

login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(result => {
if (result === true) {
this.router.navigate(['/']);
} else {
this.error = 'Username or password is incorrect';
.first()
.subscribe(
data => {
this.router.navigate([this.returnUrl]);
},
error => {
this.error = error;
this.loading = false;
}
});
});
}
}
Loading

0 comments on commit f071ba4

Please sign in to comment.