Skip to content

Commit

Permalink
Migrate AuthService to Angular
Browse files Browse the repository at this point in the history
  • Loading branch information
madhurijain160 committed May 30, 2024
1 parent 3cfb003 commit 394aa3e
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 148 deletions.
3 changes: 2 additions & 1 deletion app-new/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { AuthService } from './services/AuthService';
import { DreamsFactoryService } from './services/DreamsFactory';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
Expand All @@ -9,7 +10,7 @@ import { UpgradeModule } from '@angular/upgrade/static';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, AppRoutingModule, UpgradeModule],
providers: [DreamsFactoryService, ],
providers: [AuthService, DreamsFactoryService, ],
bootstrap: [AppComponent],
})
export class AppModule {}
111 changes: 111 additions & 0 deletions app-new/src/app/services/AuthService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HTTP_INTERCEPTORS } from '@angular/common/http';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { Router } from '@angular/router';

export const AUTH_EVENTS = {
loginSuccess: 'auth-login-success',
loginFailed: 'auth-login-failed',
logoutSuccess: 'auth-logout-success',
sessionTimeout: 'auth-session-timeout',
notAuthenticated: 'auth-not-authenticated',
notAuthorized: 'auth-not-authorized'
};

@Injectable({
providedIn: 'root'
})
export class AuthService {
private currentUserSubject: BehaviorSubject<any>;
public currentUser: Observable<any>;

constructor(private http: HttpClient, private router: Router) {
this.currentUserSubject = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('currentUser')));
this.currentUser = this.currentUserSubject.asObservable();
}

public get currentUserValue(): any {
return this.currentUserSubject.value;
}

login(credentials: any): Observable<any> {
return this.http.post<any>('/login', credentials)
.pipe(map(user => {
if (user && user.token) {
localStorage.setItem('currentUser', JSON.stringify(user));
this.currentUserSubject.next(user);
}
return user;
}), catchError(this.handleError));
}

logout() {
localStorage.removeItem('currentUser');
this.currentUserSubject.next(null);
this.router.navigate(['/login']);
}

private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
console.error('An error occurred:', error.error.message);
} else {
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
return throwError('Something bad happened; please try again later.');
}
}

@Injectable()
export class AuthInterceptor {
constructor(private router: Router) {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (currentUser && currentUser.token) {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}

return next.handle(req).pipe(catchError(err => {
if (err.status === 401) {
this.router.navigate(['/login']);
}
const error = err.error.message || err.statusText;
return throwError(error);
}));
}
}

export const authInterceptorProviders = [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];

@Injectable({
providedIn: 'root'
})
export class SessionService {
private sessionId: string | null = null;
private user: any = null;

constructor() {}

create(sessionId: string, user: any) {
this.sessionId = sessionId;
this.user = user;
}

destroy() {
this.sessionId = null;
this.user = null;
}

get isAuthenticated(): boolean {
return !!this.user;
}
}
99 changes: 99 additions & 0 deletions app-new/src/app/tests/AuthService.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
describe("fsaPreBuilt", function () {
beforeEach(module("fsaPreBuilt"));

var $rootScope, $httpBackend, AUTH_EVENTS, AuthService, Session;

beforeEach(inject(function (_$rootScope_, _$httpBackend_, _AUTH_EVENTS_, _AuthService_, _Session_) {
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
AUTH_EVENTS = _AUTH_EVENTS_;
AuthService = _AuthService_;
Session = _Session_;
}));

describe("AuthService", function () {
it("should broadcast loginSuccess on successful login", function () {
var response = { data: { id: 1, user: { name: 'TestUser' } } };
$httpBackend.whenPOST('/login').respond(200, response);
var loginSuccessSpy = sinon.spy($rootScope, '$broadcast');

AuthService.login({ username: 'test', password: 'pass' });
$httpBackend.flush();

expect(loginSuccessSpy).to.have.been.calledWith(AUTH_EVENTS.loginSuccess);
expect(Session.user).to.deep.equal(response.data.user);

loginSuccessSpy.restore();
});

it("should reject with a message on failed login", function (done) {
$httpBackend.whenPOST('/login').respond(401, { message: 'Invalid login credentials.' });

AuthService.login({ username: 'test', password: 'wrong' })
.catch(function (error) {
expect(error.message).to.equal('Invalid login credentials.');
done();
});
$httpBackend.flush();
});

it("should broadcast logoutSuccess on successful logout", function () {
$httpBackend.whenGET('/logout').respond(200);
var logoutSuccessSpy = sinon.spy($rootScope, '$broadcast');

AuthService.logout();
$httpBackend.flush();

expect(logoutSuccessSpy).to.have.been.calledWith(AUTH_EVENTS.logoutSuccess);
expect(Session.id).to.be.null;
expect(Session.user).to.be.null;

logoutSuccessSpy.restore();
});

it("should destroy session on notAuthenticated event", function () {
Session.create('sessionId', { name: 'TestUser' });
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);

expect(Session.id).to.be.null;
expect(Session.user).to.be.null;
});

it("should destroy session on sessionTimeout event", function () {
Session.create('sessionId', { name: 'TestUser' });
$rootScope.$broadcast(AUTH_EVENTS.sessionTimeout);

expect(Session.id).to.be.null;
expect(Session.user).to.be.null;
});
});

describe("AuthInterceptor", function () {
var AuthInterceptor, responseErrorSpy;

beforeEach(inject(function (_AuthInterceptor_) {
AuthInterceptor = _AuthInterceptor_;
responseErrorSpy = sinon.spy($rootScope, '$broadcast');
}));

it("should broadcast corresponding event on response error with status 401", function () {
var rejection = { status: 401 };
AuthInterceptor.responseError(rejection);

expect(responseErrorSpy).to.have.been.calledWith(AUTH_EVENTS.notAuthenticated, rejection);
responseErrorSpy.restore();
});

it("should broadcast corresponding event on response error with status 403", function () {
var rejection = { status: 403 };
AuthInterceptor.responseError(rejection);

expect(responseErrorSpy).to.have.been.calledWith(AUTH_EVENTS.notAuthorized, rejection);
responseErrorSpy.restore();
});

afterEach(function () {
responseErrorSpy.restore();
});
});
});
147 changes: 0 additions & 147 deletions browser/js/fsa/fsa-pre-built.js

This file was deleted.

0 comments on commit 394aa3e

Please sign in to comment.