Skip to content

Commit dad4d75

Browse files
committed
Fix linting
Added interface for user service response.
1 parent fb9e5c4 commit dad4d75

File tree

11 files changed

+134
-125
lines changed

11 files changed

+134
-125
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export const environment = {
22
production: true,
3-
UserServiceApiUrl: 'http://localhost:8082'
3+
UserServiceApiUrl: 'http://localhost:8082',
44
};

frontend/src/_helpers/interceptors/error.interceptor.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ import { AuthenticationService } from '../../_services/authentication.service';
77

88
@Injectable()
99
export class ErrorInterceptor implements HttpInterceptor {
10-
constructor(private authenticationService: AuthenticationService) { }
10+
constructor(private authenticationService: AuthenticationService) {}
1111

12-
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
13-
return next.handle(request).pipe(catchError(err => {
14-
if ([401, 403].includes(err.status)) {
15-
// auto logout if 401 Unauthorized or 403 Forbidden response returned from api
16-
this.authenticationService.logout();
17-
}
12+
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
13+
return next.handle(request).pipe(
14+
catchError(err => {
15+
if ([401, 403].includes(err.status)) {
16+
// auto logout if 401 Unauthorized or 403 Forbidden response returned from api
17+
this.authenticationService.logout();
18+
}
1819

19-
const error = err.error.message || err.statusText;
20-
return throwError(() => new Error(error));
21-
}))
20+
const error = err.error.message || err.statusText;
21+
return throwError(() => new Error(error));
22+
}),
23+
);
2224
}
23-
}
25+
}

frontend/src/_helpers/interceptors/jwt.interceptor.spec.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,19 @@ describe('JwtInterceptor', () => {
99
let httpMock: HttpTestingController;
1010
let httpClient: HttpClient;
1111
let mockAuthService: jasmine.SpyObj<AuthenticationService>;
12-
let mockAuthServiceNoUser: jasmine.SpyObj<AuthenticationService>;
1312

1413
beforeEach(() => {
1514
mockAuthService = jasmine.createSpyObj('AuthenticationService', ['userValue'], {
16-
userValue: { accessToken: 'fake-jwt-token' }
15+
userValue: { accessToken: 'fake-jwt-token' },
1716
});
1817

1918
TestBed.configureTestingModule({
2019
imports: [HttpClient],
2120
providers: [
2221
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
2322
{ provide: AuthenticationService, useValue: mockAuthService },
24-
provideHttpClientTesting()
25-
]
23+
provideHttpClientTesting(),
24+
],
2625
});
2726

2827
httpMock = TestBed.inject(HttpTestingController);
@@ -44,8 +43,8 @@ describe('JwtInterceptor', () => {
4443
});
4544

4645
it('should not add an Authorization header if the user is not logged in', () => {
47-
mockAuthServiceNoUser = jasmine.createSpyObj('AuthenticationService', ['userValue'], {
48-
userValue: { }
46+
mockAuthService = jasmine.createSpyObj('AuthenticationService', ['userValue'], {
47+
userValue: {},
4948
});
5049

5150
httpClient.get(`${environment.UserServiceApiUrl}/test`).subscribe();
@@ -62,4 +61,4 @@ describe('JwtInterceptor', () => {
6261

6362
expect(httpRequest.request.headers.has('Authorization')).toBeFalsy();
6463
});
65-
});
64+
});

frontend/src/_helpers/interceptors/jwt.interceptor.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ import { User } from '../../_models/user.model';
88
@Injectable()
99
export class JwtInterceptor implements HttpInterceptor {
1010
private currentUser: User | null | undefined;
11-
constructor(private authenticationService: AuthenticationService) {
11+
constructor(private authenticationService: AuthenticationService) {
1212
this.authenticationService.user$.subscribe(user => {
1313
this.currentUser = user;
14-
})
14+
});
1515
}
1616

17-
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
17+
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
1818
// add auth header with jwt if user is logged in and request is to the api url
1919
const isLoggedIn = this.currentUser?.accessToken;
2020
const isApiUrl = request.url.startsWith(environment.UserServiceApiUrl);
@@ -25,4 +25,4 @@ export class JwtInterceptor implements HttpInterceptor {
2525
}
2626
return next.handle(request);
2727
}
28-
}
28+
}

frontend/src/_models/user.model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ export class User {
55
isAdmin?: boolean;
66
createdAt?: string;
77
accessToken?: string;
8-
}
8+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export interface UServRes {
2+
message: string;
3+
data: {
4+
id: string;
5+
username: string;
6+
email: string;
7+
accessToken: string;
8+
isAdmin: boolean;
9+
createdAt: string;
10+
};
11+
}
Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,41 @@
1-
import { Injectable } from "@angular/core";
2-
import { CanActivate, Router } from "@angular/router";
3-
import { AuthenticationService } from "./authentication.service";
1+
import { Injectable } from '@angular/core';
2+
import { CanActivate, Router } from '@angular/router';
3+
import { AuthenticationService } from './authentication.service';
44
import { filter, map, Observable, of, switchMap } from 'rxjs';
5-
import { HttpClient } from "@angular/common/http";
6-
import { environment } from "../_environments/environment";
5+
import { HttpClient } from '@angular/common/http';
6+
import { environment } from '../_environments/environment';
7+
import { UServRes } from '../_models/user.service.response.interface';
78

89
@Injectable()
9-
export class AuthGuardService implements CanActivate{
10+
export class AuthGuardService implements CanActivate {
1011
constructor(
1112
private authenticationService: AuthenticationService,
1213
private http: HttpClient,
13-
private router: Router
14+
private router: Router,
1415
) {}
1516

16-
canActivate() : Observable<boolean> {
17-
return this.authenticationService.user$.pipe(
18-
filter(user => user !== undefined),
19-
switchMap((user) => { // switchMap to flatten the observable from http.get
20-
if (user === null) {
21-
// not logged in so redirect to login page with the return url
22-
this.router.navigate(['/account/login']);
23-
return of(false); // of() to return an observable to be flattened
24-
}
25-
// call to user service endpoint '/users/{user_id}' to check user is still valid
26-
return this.http.get<any>(`${environment.UserServiceApiUrl}/users/${user.id}`, { observe: 'response' })
27-
.pipe(map(response => {
28-
if (response.status === 200) {
29-
return true;
17+
canActivate(): Observable<boolean> {
18+
return this.authenticationService.user$.pipe(
19+
filter(user => user !== undefined),
20+
switchMap(user => {
21+
// switchMap to flatten the observable from http.get
22+
if (user === null) {
23+
// not logged in so redirect to login page with the return url
24+
this.router.navigate(['/account/login']);
25+
return of(false); // of() to return an observable to be flattened
3026
}
31-
return false;
32-
}))
33-
})
34-
)
35-
}
36-
}
27+
// call to user service endpoint '/users/{user_id}' to check user is still valid
28+
return this.http
29+
.get<UServRes>(`${environment.UserServiceApiUrl}/users/${user.id}`, { observe: 'response' })
30+
.pipe(
31+
map(response => {
32+
if (response.status === 200) {
33+
return true;
34+
}
35+
return false;
36+
}),
37+
);
38+
}),
39+
);
40+
}
41+
}

frontend/src/_services/authentication.service.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { Injectable } from '@angular/core';
33
import { Router } from '@angular/router';
44
import { HttpClient } from '@angular/common/http';
55
import { BehaviorSubject, Observable } from 'rxjs';
6-
import { map, switchMap, tap } from 'rxjs/operators';
6+
import { map, switchMap } from 'rxjs/operators';
77
import { environment } from '../_environments/environment';
88
import { User } from '../_models/user.model';
9+
import { UServRes } from '../_models/user.service.response.interface';
910

1011
@Injectable({ providedIn: 'root' })
1112
export class AuthenticationService {
@@ -14,7 +15,7 @@ export class AuthenticationService {
1415

1516
constructor(
1617
private router: Router,
17-
private http: HttpClient
18+
private http: HttpClient,
1819
) {
1920
this.userSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('user')!));
2021
this.user$ = this.userSubject.asObservable();
@@ -25,28 +26,34 @@ export class AuthenticationService {
2526
}
2627

2728
login(username: string, password: string) {
28-
return this.http.post<any>(`${environment.UserServiceApiUrl}/auth/login`,
29-
{ "username": username, "password": password })
30-
.pipe(map(response => {
31-
// store user details and jwt token in local storage to keep user logged in between page refreshes
32-
const data = response.data;
33-
const user: User = {
34-
id: data.id,
35-
username: data.username,
36-
email: data.email,
37-
accessToken: data.accessToken,
38-
isAdmin: data.isAdmin,
39-
createdAt: data.createdAt
40-
}
41-
localStorage.setItem('user', JSON.stringify(user));
42-
this.userSubject.next(user);
43-
return user;
44-
}));
29+
return this.http
30+
.post<UServRes>(`${environment.UserServiceApiUrl}/auth/login`, { username: username, password: password })
31+
.pipe(
32+
map(response => {
33+
// store user details and jwt token in local storage to keep user logged in between page refreshes
34+
const data = response.data;
35+
const user: User = {
36+
id: data.id,
37+
username: data.username,
38+
email: data.email,
39+
accessToken: data.accessToken,
40+
isAdmin: data.isAdmin,
41+
createdAt: data.createdAt,
42+
};
43+
localStorage.setItem('user', JSON.stringify(user));
44+
this.userSubject.next(user);
45+
return user;
46+
}),
47+
);
4548
}
4649

4750
createAccount(username: string, email: string, password: string) {
48-
return this.http.post<any>(`${environment.UserServiceApiUrl}/users`,
49-
{ "username": username, "email": email, "password": password })
51+
return this.http
52+
.post<UServRes>(`${environment.UserServiceApiUrl}/users`, {
53+
username: username,
54+
email: email,
55+
password: password,
56+
})
5057
.pipe(switchMap(() => this.login(username, password))); // auto login after registration
5158
}
5259

@@ -56,4 +63,4 @@ export class AuthenticationService {
5663
this.userSubject.next(null);
5764
this.router.navigate(['/account/login']);
5865
}
59-
}
66+
}

frontend/src/app/account/login.component.ts

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,20 @@ import { AuthenticationService } from '../../_services/authentication.service';
1212
@Component({
1313
selector: 'app-login',
1414
standalone: true,
15-
imports: [
16-
RouterLink,
17-
FormsModule,
18-
InputTextModule,
19-
ButtonModule,
20-
SelectButtonModule,
21-
PasswordModule,
22-
ToastModule,
23-
],
15+
imports: [RouterLink, FormsModule, InputTextModule, ButtonModule, SelectButtonModule, PasswordModule, ToastModule],
2416
providers: [MessageService, AuthenticationService],
2517
templateUrl: './login.component.html',
2618
styleUrl: './account.component.css',
2719
})
2820
export class LoginComponent {
2921
constructor(
30-
private messageService: MessageService,
22+
private messageService: MessageService,
3123
private authenticationService: AuthenticationService,
3224
private router: Router,
33-
private route: ActivatedRoute
25+
private route: ActivatedRoute,
3426
) {
3527
//redirect to home if already logged in
36-
if (this.authenticationService.userValue) {
28+
if (this.authenticationService.userValue) {
3729
this.router.navigate(['/']);
3830
}
3931
}
@@ -50,31 +42,29 @@ export class LoginComponent {
5042
this.isProcessingLogin = true;
5143

5244
// authenticationService returns an observable that we can subscribe to
53-
this.authenticationService.login(this.userForm.username, this.userForm.password)
54-
.pipe()
55-
.subscribe({
56-
next: () => {
57-
// get return url from route parameters or default to '/'
58-
const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
59-
this.router.navigate([returnUrl]);
60-
},
61-
error: error => {
62-
console.error(error);
63-
this.isProcessingLogin = false;
64-
let errorMessage = 'An unknown error occurred';
65-
if (error.status === 400) {
66-
errorMessage = 'Missing Fields';
67-
}
68-
else if (error.status === 401) {
69-
errorMessage = 'Invalid username or password';
70-
}
71-
else if (error.status === 500) {
72-
errorMessage = 'Database Server Error';
73-
}
74-
this.messageService.add({ severity: 'error', summary: 'Log In Error', detail: errorMessage });
75-
}
76-
});
77-
45+
this.authenticationService
46+
.login(this.userForm.username, this.userForm.password)
47+
.pipe()
48+
.subscribe({
49+
next: () => {
50+
// get return url from route parameters or default to '/'
51+
const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
52+
this.router.navigate([returnUrl]);
53+
},
54+
error: error => {
55+
console.error(error);
56+
this.isProcessingLogin = false;
57+
let errorMessage = 'An unknown error occurred';
58+
if (error.status === 400) {
59+
errorMessage = 'Missing Fields';
60+
} else if (error.status === 401) {
61+
errorMessage = 'Invalid username or password';
62+
} else if (error.status === 500) {
63+
errorMessage = 'Database Server Error';
64+
}
65+
this.messageService.add({ severity: 'error', summary: 'Log In Error', detail: errorMessage });
66+
},
67+
});
7868
} else {
7969
console.log('Invalid form');
8070
}

0 commit comments

Comments
 (0)