Skip to content
This repository has been archived by the owner on Oct 26, 2024. It is now read-only.

Commit

Permalink
refactor(select-union): extract to separate component
Browse files Browse the repository at this point in the history
  • Loading branch information
tsvetkovv committed May 2, 2020
1 parent bf14955 commit 0d55b01
Show file tree
Hide file tree
Showing 23 changed files with 385 additions and 222 deletions.
16 changes: 16 additions & 0 deletions src/_constants/unions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {InjectionToken} from '@angular/core';

export const unions: UnionMap = {
mydata: {key: 'mydata', name: 'My Data', url: 'https://mydata.webtree.org/applyToken'},
imprint: {key: 'imprint', name: 'Imprint', url: 'https://imprint.webtree.org/applyToken'}
};

export interface Union {
key: string;
name: string;
url: string;
}

export type UnionMap = Record<string, Union>;

export const UNIONS_TOKEN = new InjectionToken<UnionMap>('unions');
30 changes: 30 additions & 0 deletions src/_helpers/login.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot} from '@angular/router';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {AuthenticationService} from '../_services/authentication.service';
import {TokenService} from '../_services/token.service';

@Injectable({providedIn: 'root'})
export class LoginGuard implements CanActivate {
constructor(
private tokenService: TokenService,
private authenticationService: AuthenticationService,
) {
}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
if (!this.tokenService.tokenExists()) {
return true;
}

return this.tokenService.isTokenValid().pipe(
map(isValid => {
if (isValid) {
return !this.authenticationService.redirectToUnionIfNeeded(route);
}
return true;
})
);
}
}
1 change: 0 additions & 1 deletion src/_models/index.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/_services/alert.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {Injectable} from '@angular/core';
import {MatSnackBar} from '@angular/material';

@Injectable()
@Injectable({ providedIn: 'root'})
export class AlertService {

constructor(private snackBar: MatSnackBar) {
Expand Down
72 changes: 50 additions & 22 deletions src/_services/authentication.service.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,67 @@
import {Injectable} from '@angular/core';
import 'rxjs/add/operator/map';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {Inject, Injectable} from '@angular/core';
import {Observable, of} from 'rxjs';
import {catchError, map} from 'rxjs/operators';
import {TokenService} from './token.service';
import {HttpClient} from '@angular/common/http';
import {environment} from '../environments/environment';
import {User} from '../_models';
import {User} from '../_models/User';
import {AlertService} from './alert.service';
import {ActivatedRouteSnapshot} from '@angular/router';
import {Union, UnionMap, UNIONS_TOKEN} from '../_constants/unions';
import {DOCUMENT} from '@angular/common';

@Injectable()
@Injectable({providedIn: 'root'})
export class AuthenticationService {
constructor(private http: HttpClient,
private tokenService: TokenService) {
private alertService: AlertService,
private tokenService: TokenService,
@Inject(UNIONS_TOKEN) private unions: UnionMap,
@Inject(DOCUMENT) private document: Document,
) {
}

login(user: User) {
return this.http.post(environment.backendUrl + 'token/new', user, {responseType: 'text'});
login(user: User): Observable<string | null> {
return this.http.post<{ token: string }>(environment.backendUrl + 'token/new', user)
.pipe(
map(res => {
if ('token' in res) {
return res.token;
}
return null;
}),
catchError((error: HttpErrorResponse) => {
console.log(error);
if (error.status === 401) {
this.alertService.error(error.error);
return of(null);
}
})
);
}

logout() {
this.tokenService.removeToken();
}

async isAuthorized(): Promise<boolean> {
if (!this.tokenService.tokenExists()) {
return Promise.resolve(this.tokenService.tokenExists());
}
redirectToUnionIfNeeded(route: ActivatedRouteSnapshot): boolean {
const returnUnion = route.queryParamMap.get('returnUnion');

return this.http.post(environment.backendUrl + 'checkToken', this.tokenService.getToken())
.toPromise()
.then(() => {
if (typeof returnUnion === 'string') {
if (returnUnion in this.unions) {
this.redirectToUnion(this.unions[returnUnion]);
return true;
}).catch((err) => {
if (err.status !== 401) {
console.error(err);
}
this.tokenService.removeToken();
return false;
});
} else {
this.alertService.error(`Unknown union: ${returnUnion}`);
}
}

return false;
}

redirectToUnion({url}: Union): void {
const token = this.tokenService.getToken();
const tokenizedUrl = `${url}#token=${token}`;

this.document.location.href = tokenizedUrl;
}
}
12 changes: 12 additions & 0 deletions src/_services/browser-storage.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {TestBed} from '@angular/core/testing';

import {BrowserStorageService} from './browser-storage.service';

describe('BrowserStorageServiceService', () => {
beforeEach(() => TestBed.configureTestingModule({}));

it('should be created', () => {
const service: BrowserStorageService = TestBed.get(BrowserStorageService);
expect(service).toBeTruthy();
});
});
29 changes: 29 additions & 0 deletions src/_services/browser-storage.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Inject, Injectable, InjectionToken } from '@angular/core';

export const BROWSER_STORAGE = new InjectionToken<Storage>('Browser Storage', {
providedIn: 'root',
factory: () => localStorage
});

@Injectable({
providedIn: 'root'
})
export class BrowserStorageService {
constructor(@Inject(BROWSER_STORAGE) public storage: Storage) {}

get(key: string) {
return this.storage.getItem(key);
}

set(key: string, value: string) {
return this.storage.setItem(key, value);
}

remove(key: string) {
return this.storage.removeItem(key);
}

clear() {
this.storage.clear();
}
}
4 changes: 0 additions & 4 deletions src/_services/index.ts

This file was deleted.

31 changes: 27 additions & 4 deletions src/_services/token.service.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,45 @@
import {Injectable} from '@angular/core';
import {Observable, of} from 'rxjs';
import {environment} from '../environments/environment';
import {catchError, mapTo} from 'rxjs/operators';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {BrowserStorageService} from './browser-storage.service';

@Injectable()
@Injectable({providedIn: 'root'})
export class TokenService {
private tokenName = 'token';

constructor(private http: HttpClient,
private storage: BrowserStorageService) {
}

tokenExists(): boolean {
return !!this.getToken();
}

getToken(): string {
return localStorage.getItem(this.tokenName);
return this.storage.get(this.tokenName);
}

saveToken(token: string) {
localStorage.setItem(this.tokenName, token);
this.storage.set(this.tokenName, token);
}

removeToken(): void {
localStorage.removeItem('token');
this.storage.remove('token');
}

isTokenValid(): Observable<boolean> {
return this.http.post(environment.backendUrl + 'checkToken', this.getToken())
.pipe(
mapTo(true),
catchError((err: HttpErrorResponse) => {
if (err.status !== 401) {
console.error(err);
}
this.removeToken();
return of(false);
})
);
}
}
22 changes: 17 additions & 5 deletions src/_services/user.service.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import {Injectable} from '@angular/core';

import {User} from '../_models';
import {HttpClient} from '@angular/common/http';
import {User} from '../_models/User';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {Observable} from 'rxjs/Observable';
import {environment} from '../environments/environment';
import {catchError} from 'rxjs/operators';
import {of, throwError} from 'rxjs';

@Injectable()
@Injectable({providedIn: 'root'})
export class UserService {
constructor(private http: HttpClient) {
}

create(user: User): Observable<User> {
create(user: User): Observable<User | null> {
const url = environment.backendUrl + 'user/register';
return this.http.post<User>(url, user);

return this.http.post<User | null>(url, user).pipe(
catchError((error: HttpErrorResponse) => {
console.error(error);
if (error.status === 400) {
return of(null);
} else {
return throwError(error);
}
})
);
}
}
12 changes: 8 additions & 4 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {RegisterComponent} from '../register';
import {LoginComponent} from '../login';
import {RegisterComponent} from '../register/register.component';
import {LoginComponent} from '../login/login.component';
import {SelectUnionComponent} from '../select-union/select-union.component';
import {LoginGuard} from '../_helpers/login.guard';

const routes: Routes = [
{path: 'register', component: RegisterComponent},
{path: 'login', component: LoginComponent},
{path: 'login', component: LoginComponent, canActivate: [LoginGuard]},
{path: 'select-union', component: SelectUnionComponent},
{path: '**', redirectTo: 'login'}
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
export class AppRoutingModule {
}
24 changes: 12 additions & 12 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {HttpClientModule} from '@angular/common/http';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';

import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {RegisterComponent} from '../register';
import {AlertService, AuthenticationService, TokenService, UserService} from '../_services';
import {HttpClientModule} from '@angular/common/http';
import {Subject} from 'rxjs';
import {LoginComponent} from '../login';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {RegisterComponent} from '../register/register.component';
import {LoginComponent} from '../login/login.component';
import {MaterialModule} from './material.module';
import {unions, UNIONS_TOKEN} from '../_constants/unions';
import {SelectUnionComponent} from '../select-union/select-union.component';

@NgModule({
declarations: [
AppComponent,
LoginComponent,
RegisterComponent,
SelectUnionComponent,
],
imports: [
BrowserModule,
Expand All @@ -28,11 +29,10 @@ import {MaterialModule} from './material.module';
MaterialModule
],
providers: [
UserService,
AlertService,
AuthenticationService,
TokenService,
Subject
{
provide: UNIONS_TOKEN,
useValue: unions
}
],
bootstrap: [AppComponent]
})
Expand Down
2 changes: 1 addition & 1 deletion src/environments/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

export const environment = {
production: false,
backendUrl: 'http://localhost:9000/rest/',
backendUrl: 'https://auth-api.webtree.org/rest/',
};

/*
Expand Down
1 change: 0 additions & 1 deletion src/login/index.ts

This file was deleted.

Loading

0 comments on commit 0d55b01

Please sign in to comment.