From 6e7d89a4bdc32fde05acb139e9905e041bcb5c89 Mon Sep 17 00:00:00 2001 From: FORESTIER Fabien <fabien.forestier@soprasteria.com> Date: Fri, 4 Jan 2019 16:40:34 +0100 Subject: [PATCH] Rafacto put everything user related in the user module + test --- src/app/app.component.ts | 4 +- src/app/app.module.ts | 6 +- .../contact/contact.component.spec.ts | 12 +- .../components/contact/contact.component.ts | 16 +- src/app/core/components/index.ts | 6 +- .../main/header/header.component.spec.ts | 4 +- .../main/side-menu/side-menu.component.html | 6 +- .../side-menu/side-menu.component.spec.ts | 8 +- .../main/side-menu/side-menu.component.ts | 11 +- src/app/core/core-routing.module.ts | 16 +- src/app/core/core.module.ts | 8 - src/app/core/handlers/errors-handler.ts | 2 +- src/app/core/models/auth.model.ts | 66 --- src/app/core/models/index.ts | 5 +- src/app/core/services/auth.service.ts | 79 --- src/app/core/services/index.ts | 4 +- src/app/routes.ts | 8 +- .../auth}/login/login.component.html | 0 .../auth}/login/login.component.scss | 2 +- .../auth}/login/login.component.spec.ts | 8 +- .../components/auth}/login/login.component.ts | 6 +- .../auth}/sign-up/sign-up.component.html | 0 .../auth}/sign-up/sign-up.component.scss | 2 +- .../auth}/sign-up/sign-up.component.spec.ts | 11 +- .../auth}/sign-up/sign-up.component.ts | 22 +- src/app/user/components/index.ts | 10 +- .../user-account.component.spec.ts | 25 - .../user-profil/user-profi.component.spec.ts | 456 ++++++++++++++++++ .../user-profil.component.html} | 16 +- .../user-profil.component.scss} | 0 .../user-profil.component.ts} | 69 +-- .../user-services/user-services.component.ts | 12 +- .../guards/authenticated.guard.ts | 6 +- src/app/{core => user}/guards/index.ts | 2 +- .../interceptors/auth-interceptor.ts | 0 src/app/user/models/index.ts | 3 +- src/app/user/models/user.model.ts | 67 +++ src/app/user/services/user.service.ts | 68 ++- src/app/user/user-routing.module.ts | 23 +- src/app/user/user.module.ts | 11 +- .../validators/password.validator.ts | 0 src/i18n/messages.en.xlf | 20 +- src/i18n/messages.fr.xlf | 20 +- 43 files changed, 774 insertions(+), 346 deletions(-) delete mode 100644 src/app/core/models/auth.model.ts delete mode 100644 src/app/core/services/auth.service.ts rename src/app/{core/components => user/components/auth}/login/login.component.html (100%) rename src/app/{core/components => user/components/auth}/login/login.component.scss (91%) rename src/app/{core/components => user/components/auth}/login/login.component.spec.ts (86%) rename src/app/{core/components => user/components/auth}/login/login.component.ts (91%) rename src/app/{core/components => user/components/auth}/sign-up/sign-up.component.html (100%) rename src/app/{core/components => user/components/auth}/sign-up/sign-up.component.scss (91%) rename src/app/{core/components => user/components/auth}/sign-up/sign-up.component.spec.ts (98%) rename src/app/{core/components => user/components/auth}/sign-up/sign-up.component.ts (89%) delete mode 100644 src/app/user/components/user-account/user-account.component.spec.ts create mode 100644 src/app/user/components/user-profil/user-profi.component.spec.ts rename src/app/user/components/{user-account/user-account.component.html => user-profil/user-profil.component.html} (95%) rename src/app/user/components/{user-account/user-account.component.scss => user-profil/user-profil.component.scss} (100%) rename src/app/user/components/{user-account/user-account.component.ts => user-profil/user-profil.component.ts} (82%) rename src/app/{core => user}/guards/authenticated.guard.ts (70%) rename src/app/{core => user}/guards/index.ts (82%) rename src/app/{core => user}/interceptors/auth-interceptor.ts (100%) rename src/app/{core => user}/validators/password.validator.ts (100%) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9363d46f..0f71a410 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,9 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { Router, NavigationEnd, ActivatedRoute } from '../../node_modules/@angular/router'; -import { pairwise, filter, map, mergeMap } from 'rxjs/operators'; +import { filter, map } from 'rxjs/operators'; import { AppRoutes } from './routes'; import { Angulartics2Piwik } from 'angulartics2/piwik'; -import { NavigationHistoryService, AuthService } from './core/services'; +import { NavigationHistoryService } from './core/services'; import { Title } from '@angular/platform-browser'; @Component({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8ae0af99..890e34eb 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,11 +9,11 @@ import { CoreModule } from './core/core.module'; import { EditorialisationModule } from './editorialisation/editorialisation.module'; import { Angulartics2Module } from 'angulartics2'; import { Angulartics2Piwik } from 'angulartics2/piwik'; -import { AuthService } from './core/services'; import { UserModule } from './user/user.module'; +import { UserService } from './user/services'; // Function used by APP_INITIALIZER before the app start: init user info / statut (expect a promise) -export function loadUser(authService: AuthService) { +export function loadUser(authService: UserService) { return (): Promise<any> => { return new Promise((resolve, reject) => { authService.setUserInfo(); @@ -40,7 +40,7 @@ export function loadUser(authService: AuthService) { { provide: APP_INITIALIZER, useFactory: loadUser, - deps: [AuthService], + deps: [UserService], multi: true, }, ], diff --git a/src/app/core/components/contact/contact.component.spec.ts b/src/app/core/components/contact/contact.component.spec.ts index 7876beb9..0059db37 100644 --- a/src/app/core/components/contact/contact.component.spec.ts +++ b/src/app/core/components/contact/contact.component.spec.ts @@ -1,11 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ContactComponent } from './contact.component'; -import { ReactiveFormsModule, FormControl, AbstractControl } from '@angular/forms'; -import { EmailService, NotificationService, NavigationHistoryService, AuthService } from '../../services'; +import { ReactiveFormsModule, AbstractControl } from '@angular/forms'; +import { EmailService, NotificationService, NavigationHistoryService } from '../../services'; import { BehaviorSubject, of } from 'rxjs'; -import { Email } from '../../models'; import { RouterTestingModule } from '@angular/router/testing'; +import { UserService } from '../../../user/services'; export class NotificationServiceMock { @@ -29,7 +29,7 @@ export class EmailServiceMock { } -export class AuthServiceMock { +export class UserServiceMock { constructor() { } @@ -62,8 +62,8 @@ describe('ContactComponent', () => { useClass: EmailServiceMock, }, { - provide: AuthService, - useClass: AuthServiceMock, + provide: UserService, + useClass: UserServiceMock, }, { provide: NotificationService, diff --git a/src/app/core/components/contact/contact.component.ts b/src/app/core/components/contact/contact.component.ts index 2979ce4f..60c56b41 100644 --- a/src/app/core/components/contact/contact.component.ts +++ b/src/app/core/components/contact/contact.component.ts @@ -1,10 +1,12 @@ import { Component, OnInit } from '@angular/core'; -import { FormGroup, FormBuilder, Validators, FormControl, AbstractControl } from '@angular/forms'; -import { EmailService, NotificationService, NavigationHistoryService, AuthService } from '../../services'; -import { Email, User } from '../../models'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { EmailService, NotificationService, NavigationHistoryService } from '../../services'; +import { Email } from '../../models'; import { subjects as Subjects, feedbackMessages } from '../../../../i18n/contact/contact'; import { Router, ActivatedRoute } from '@angular/router'; import { AppRoutes } from '../../../routes'; +import { User } from '../../../user/models'; +import { UserService } from '../../../user/services'; @Component({ selector: 'app-contact', @@ -26,7 +28,7 @@ export class ContactComponent implements OnInit { private _navigationHistoryService: NavigationHistoryService, private _router: Router, private _route: ActivatedRoute, - private _authService: AuthService, + private _userService: UserService, ) { this.form = this._fb.group( { @@ -46,8 +48,8 @@ export class ContactComponent implements OnInit { ngOnInit() { // if user authenticated then initialize the form with its name and email - if (this._authService.userIsSignedIn) { - this.user = this._authService.user; + if (this._userService.userIsSignedIn) { + this.user = this._userService.user; this.firstname.patchValue(this.user.firstName); this.lastname.patchValue(this.user.lastName); this.email.patchValue(this.user.email); @@ -163,7 +165,7 @@ export class ContactComponent implements OnInit { } get userIsSignedIn() { - return this._authService.userIsSignedIn; + return this._userService.userIsSignedIn; } toUppercase(controlName: string) { diff --git a/src/app/core/components/index.ts b/src/app/core/components/index.ts index 42d2f9fe..ccd96c57 100644 --- a/src/app/core/components/index.ts +++ b/src/app/core/components/index.ts @@ -3,13 +3,11 @@ import { MainComponent } from './main/main.component'; import { FooterComponent } from './main/footer/footer.component'; import { NotificationsComponent } from './notifications/notifications.component'; import { ErrorComponent } from './error/error.component'; -import { LoginComponent } from './login/login.component'; import { ContactComponent } from './contact/contact.component'; -import { SignUpComponent } from './sign-up/sign-up.component'; import { SideMenuComponent } from './main/side-menu/side-menu.component'; export { HeaderComponent, MainComponent, FooterComponent, NotificationsComponent, - ErrorComponent, LoginComponent, ContactComponent, SignUpComponent }; + ErrorComponent, ContactComponent }; // tslint:disable-next-line:variable-name export const CoreComponents = [ @@ -19,7 +17,5 @@ export const CoreComponents = [ NotificationsComponent, ErrorComponent, ContactComponent, - LoginComponent, - SignUpComponent, SideMenuComponent, ]; diff --git a/src/app/core/components/main/header/header.component.spec.ts b/src/app/core/components/main/header/header.component.spec.ts index 52db0f6e..8eec4ec0 100644 --- a/src/app/core/components/main/header/header.component.spec.ts +++ b/src/app/core/components/main/header/header.component.spec.ts @@ -2,10 +2,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { DebugElement } from '@angular/core'; import { RouterTestingModule } from '@angular/router/testing'; -import { FormsModule } from '@angular/forms'; - import { HeaderComponent } from './header.component'; -import { AuthService, ErrorService, NotificationService } from '../../../services'; +import { ErrorService, NotificationService } from '../../../services'; import { Router } from '@angular/router'; import { DatasetResearchService, ElasticsearchService } from '../../../../geosource/services'; import { MockComponent } from 'ng2-mock-component'; diff --git a/src/app/core/components/main/side-menu/side-menu.component.html b/src/app/core/components/main/side-menu/side-menu.component.html index 22f17bc9..b2df2543 100644 --- a/src/app/core/components/main/side-menu/side-menu.component.html +++ b/src/app/core/components/main/side-menu/side-menu.component.html @@ -66,7 +66,7 @@ </a> </li> <li *ngIf="userIsSignedIn"> - <a href preventDefault [routerLink]="['/', AppRoutes.userAccount.uri]"> + <a href preventDefault [routerLink]="['/', AppRoutes.userProfil.uri]"> <span class="username">{{ username }}</span> </a> </li> @@ -76,8 +76,8 @@ </a> </li> <li *ngIf="userIsSignedIn"> - <a href preventDefault [routerLink]="['/', AppRoutes.userAccount.uri]" routerLinkActive="active-link"> - <span i18n="@@header.userAccount">My account</span> + <a href preventDefault [routerLink]="['/', AppRoutes.userProfil.uri]" routerLinkActive="active-link"> + <span i18n="@@header.userProfil">My profil</span> </a> </li> <li *ngIf="userIsSignedIn"> diff --git a/src/app/core/components/main/side-menu/side-menu.component.spec.ts b/src/app/core/components/main/side-menu/side-menu.component.spec.ts index 5342d568..91098d92 100644 --- a/src/app/core/components/main/side-menu/side-menu.component.spec.ts +++ b/src/app/core/components/main/side-menu/side-menu.component.spec.ts @@ -1,11 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SideMenuComponent } from './side-menu.component'; -import { AuthService } from '../../../services'; import { MockComponent } from 'ng2-mock-component'; import { RouterTestingModule } from '@angular/router/testing'; +import { UserService } from '../../../../user/services'; -export class AuthServiceMock { +export class UserServiceMock { constructor() { } @@ -30,8 +30,8 @@ describe('SideMenuComponent', () => { ], providers: [ { - provide: AuthService, - useClass: AuthServiceMock, + provide: UserService, + useClass: UserServiceMock, }, ], }) diff --git a/src/app/core/components/main/side-menu/side-menu.component.ts b/src/app/core/components/main/side-menu/side-menu.component.ts index 3990956d..e11eeeca 100644 --- a/src/app/core/components/main/side-menu/side-menu.component.ts +++ b/src/app/core/components/main/side-menu/side-menu.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit, Input } from '@angular/core'; import { AppRoutes } from '../../../../routes'; import { Router } from '@angular/router'; -import { AuthService } from '../../../services'; import { environment } from '../../../../../environments/environment'; +import { UserService } from '../../../../user/services'; @Component({ selector: 'app-side-menu', @@ -17,7 +17,7 @@ export class SideMenuComponent implements OnInit { constructor( private _router: Router, - private _authService: AuthService, + private _userService: UserService, ) { } ngOnInit() { @@ -29,15 +29,16 @@ export class SideMenuComponent implements OnInit { } signOut() { - this._authService.resetAuth(); + this._userService.resetAuth(); + this._router.navigate(['/', AppRoutes.signin.uri]); } get userIsSignedIn() { - return this._authService.userIsSignedIn; + return this._userService.userIsSignedIn; } get username() { - return `${this._authService.user.firstName} ${this._authService.user.lastName}`; + return `${this._userService.user.firstName} ${this._userService.user.lastName}`; } } diff --git a/src/app/core/core-routing.module.ts b/src/app/core/core-routing.module.ts index 042cf903..8b73098a 100644 --- a/src/app/core/core-routing.module.ts +++ b/src/app/core/core-routing.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AppRoutes } from '../routes'; -import { ErrorComponent, ContactComponent, LoginComponent, SignUpComponent } from './components'; +import { ErrorComponent, ContactComponent } from './components'; export const routes: Routes = [ { @@ -9,20 +9,6 @@ export const routes: Routes = [ redirectTo: AppRoutes.home.uri, pathMatch: 'full', }, - { - path: AppRoutes.signin.uri, - component: LoginComponent, - data: { - title: AppRoutes.signin.title, - }, - }, - { - path: AppRoutes.signup.uri, - component: SignUpComponent, - data: { - title: AppRoutes.signup.title, - }, - }, { path: AppRoutes.error.uri, component: ErrorComponent, diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 7dbcc589..d035eac2 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -9,9 +9,7 @@ import { HttpErrorResponseInterceptor } from './interceptors/http-error-response import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { CoreServices } from './services'; import { ReactiveFormsModule, FormsModule } from '@angular/forms'; -import { AuthInterceptor } from './interceptors/auth-interceptor'; import { GeosourceModule } from '../geosource/geosource.module'; -import { CoreGuards } from './guards'; @NgModule({ imports: [ @@ -25,7 +23,6 @@ import { CoreGuards } from './guards'; declarations: [...CoreComponents], providers: [ ...CoreServices, - ...CoreGuards, { provide: ErrorHandler, useClass: ErrorsHandler, @@ -35,11 +32,6 @@ import { CoreGuards } from './guards'; useClass: HttpErrorResponseInterceptor, multi: true, }, - { - provide: HTTP_INTERCEPTORS, - useClass: AuthInterceptor, - multi: true, - }, ], exports: [MainComponent, NotificationsComponent], }) diff --git a/src/app/core/handlers/errors-handler.ts b/src/app/core/handlers/errors-handler.ts index 76892001..e44ea179 100644 --- a/src/app/core/handlers/errors-handler.ts +++ b/src/app/core/handlers/errors-handler.ts @@ -4,7 +4,7 @@ import { ErrorHandler, Injectable } from '@angular/core'; export class ErrorsHandler implements ErrorHandler { handleError(error: Error) { - // console.log(error); + console.log(error); return; } diff --git a/src/app/core/models/auth.model.ts b/src/app/core/models/auth.model.ts deleted file mode 100644 index 3d771ba4..00000000 --- a/src/app/core/models/auth.model.ts +++ /dev/null @@ -1,66 +0,0 @@ -export class User { - id: number; - firstName: string; - lastName: string; - email: string; - username: string; - - // payload is the decrypted payload of the JWT token - constructor(payload) { - if (payload) { - this.id = payload.id; - this.firstName = payload.firstName; - this.lastName = payload.lastName; - this.email = payload.email; - this.username = payload.username; - } - } -} - -export interface ILoginResponse { - token: string; -} - -export interface ICreateAccountForm { - firstName: string; - lastName: string; - email: string; - emailConfirmation: string; - password: string; - passwordConfirmation: string; - acceptMessages: boolean; - entreprise: string; - address: string; - zipcode: string; - city: string; - country: string; - cgu: boolean; -} - -export class LegacyAccount { - firstName: string; - lastName: string; - username: string; - email: string; - password: string; - acceptMessages: boolean; - entreprise: string; - address: string; - zipcode: string; - city: string; - country: string; - - constructor(form: ICreateAccountForm) { - this.firstName = form.firstName; - this.lastName = form.lastName; - this.username = form.email; - this.email = form.email; - this.password = form.password; - this.acceptMessages = form.acceptMessages; - this.entreprise = form.entreprise; - this.address = form.address; - this.zipcode = form.zipcode; - this.city = form.city; - this.country = form.country; - } -} diff --git a/src/app/core/models/index.ts b/src/app/core/models/index.ts index 9244fa97..4c6371bf 100644 --- a/src/app/core/models/index.ts +++ b/src/app/core/models/index.ts @@ -1,9 +1,8 @@ import { Notification, INotification } from './notification.model'; import { IMatomoResponse } from './matomo.model'; import { IContactForm, Email } from './email.model'; -import { User, ILoginResponse, ICreateAccountForm, LegacyAccount } from './auth.model'; export { - Notification, INotification, User, IMatomoResponse, ILoginResponse, - IContactForm, Email, ICreateAccountForm, LegacyAccount, + Notification, INotification, IMatomoResponse, + IContactForm, Email, }; diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts deleted file mode 100644 index c4080cbd..00000000 --- a/src/app/core/services/auth.service.ts +++ /dev/null @@ -1,79 +0,0 @@ - -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import * as JwtDecode from 'jwt-decode'; -import { map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { ILoginResponse, User, LegacyAccount } from '../models'; -import { environment } from '../../../environments/environment'; - -@Injectable() -export class AuthService { - - private _user: User = null; - - constructor( - private _http: HttpClient, - ) { } - - login(loginForm): Observable<boolean> { - return this._http.post<ILoginResponse>(environment.legacyLoginEndpoint, loginForm).pipe( - map( - (res) => { - return this.setSession(res); - }, - (err) => { - throw err; - }, - ), - ); - } - - createAccount(legacyAccount: LegacyAccount): Observable<boolean> { - return this._http.post<any>(`${environment.middlewareLegacyAuth}user/`, legacyAccount).pipe( - map( - (res) => { - return true; - }, - (err) => { - throw err; - }, - ), - ); - } - - private setSession(authResult): boolean { - let success = false; - if (authResult && authResult.token) { - localStorage.setItem('token', authResult.token); - this.setUserInfo(); - success = true; - } else { - this.resetAuth(); - } - - return success; - } - - setUserInfo() { - const token = localStorage.getItem('token'); - try { - const payload = JwtDecode(token); - this._user = new User(payload); - } catch (error) { - } - } - - resetAuth() { - localStorage.removeItem('token'); - this._user = null; - } - - get user() { - return this._user; - } - - get userIsSignedIn() { - return this._user != null; - } -} diff --git a/src/app/core/services/index.ts b/src/app/core/services/index.ts index 1fb610a3..cdf460b5 100644 --- a/src/app/core/services/index.ts +++ b/src/app/core/services/index.ts @@ -4,8 +4,7 @@ import { MatomoService } from './matomo.service'; import { NavigationHistoryService } from './navigation-history.service'; import { StorageService } from './storage.service'; import { EmailService } from './email.service'; -import { AuthService } from './auth.service'; -export { ErrorService, NotificationService, MatomoService, NavigationHistoryService, EmailService, AuthService }; +export { ErrorService, NotificationService, MatomoService, NavigationHistoryService, EmailService }; // tslint:disable-next-line:variable-name export const CoreServices = [ @@ -15,5 +14,4 @@ export const CoreServices = [ NavigationHistoryService, StorageService, EmailService, - AuthService, ]; diff --git a/src/app/routes.ts b/src/app/routes.ts index 5800cc0f..8625452d 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -28,11 +28,11 @@ export const AppRoutes = { en: 'My services', }, }, - userAccount: { - uri: 'mon-compte', + userProfil: { + uri: 'mon-profil', title: { - fr: 'Mon compte', - en: 'My account', + fr: 'Mon profil', + en: 'My profil', }, }, home: { diff --git a/src/app/core/components/login/login.component.html b/src/app/user/components/auth/login/login.component.html similarity index 100% rename from src/app/core/components/login/login.component.html rename to src/app/user/components/auth/login/login.component.html diff --git a/src/app/core/components/login/login.component.scss b/src/app/user/components/auth/login/login.component.scss similarity index 91% rename from src/app/core/components/login/login.component.scss rename to src/app/user/components/auth/login/login.component.scss index 612ee1cc..e9884c5c 100644 --- a/src/app/core/components/login/login.component.scss +++ b/src/app/user/components/auth/login/login.component.scss @@ -1,4 +1,4 @@ -@import '../../../../scss/variables.scss'; +@import '../../../../../scss/variables.scss'; h1 { font-size: 2rem; diff --git a/src/app/core/components/login/login.component.spec.ts b/src/app/user/components/auth/login/login.component.spec.ts similarity index 86% rename from src/app/core/components/login/login.component.spec.ts rename to src/app/user/components/auth/login/login.component.spec.ts index 5a44fe2c..2e8025e4 100644 --- a/src/app/core/components/login/login.component.spec.ts +++ b/src/app/user/components/auth/login/login.component.spec.ts @@ -2,10 +2,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { LoginComponent } from './login.component'; import { ReactiveFormsModule } from '@angular/forms'; -import { AuthService } from '../../services'; import { RouterTestingModule } from '@angular/router/testing'; +import { UserService } from '../../../services'; -export class AuthServiceMock { +export class UserServiceMock { constructor() { } @@ -28,8 +28,8 @@ describe('LoginComponent', () => { declarations: [LoginComponent], providers: [ { - provide: AuthService, - useClass: AuthServiceMock, + provide: UserService, + useClass: UserServiceMock, }, ], }) diff --git a/src/app/core/components/login/login.component.ts b/src/app/user/components/auth/login/login.component.ts similarity index 91% rename from src/app/core/components/login/login.component.ts rename to src/app/user/components/auth/login/login.component.ts index 4f1c6366..b43ecccc 100644 --- a/src/app/core/components/login/login.component.ts +++ b/src/app/user/components/auth/login/login.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { AuthService } from '../../services'; import { Router } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; +import { UserService } from '../../../services'; @Component({ selector: 'app-login', @@ -16,7 +16,7 @@ export class LoginComponent implements OnInit { constructor( private _fb: FormBuilder, - private _authService: AuthService, + private _userService: UserService, private _router: Router, ) { @@ -31,7 +31,7 @@ export class LoginComponent implements OnInit { login() { if (this.form.valid) { this.form.disable(); - this._authService.login(this.form.value).subscribe( + this._userService.login(this.form.value).subscribe( (res) => { this.form.enable(); this.errorStatus = null; diff --git a/src/app/core/components/sign-up/sign-up.component.html b/src/app/user/components/auth/sign-up/sign-up.component.html similarity index 100% rename from src/app/core/components/sign-up/sign-up.component.html rename to src/app/user/components/auth/sign-up/sign-up.component.html diff --git a/src/app/core/components/sign-up/sign-up.component.scss b/src/app/user/components/auth/sign-up/sign-up.component.scss similarity index 91% rename from src/app/core/components/sign-up/sign-up.component.scss rename to src/app/user/components/auth/sign-up/sign-up.component.scss index e1b369d9..a18f05de 100644 --- a/src/app/core/components/sign-up/sign-up.component.scss +++ b/src/app/user/components/auth/sign-up/sign-up.component.scss @@ -1,4 +1,4 @@ -@import '../../../../scss/variables.scss'; +@import '../../../../../scss/variables.scss'; .signup-form-container { background-color: white; diff --git a/src/app/core/components/sign-up/sign-up.component.spec.ts b/src/app/user/components/auth/sign-up/sign-up.component.spec.ts similarity index 98% rename from src/app/core/components/sign-up/sign-up.component.spec.ts rename to src/app/user/components/auth/sign-up/sign-up.component.spec.ts index 7c5d4f24..4eff270c 100644 --- a/src/app/core/components/sign-up/sign-up.component.spec.ts +++ b/src/app/user/components/auth/sign-up/sign-up.component.spec.ts @@ -3,10 +3,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { SignUpComponent } from './sign-up.component'; import { AbstractControl, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; -import { NotificationService, AuthService } from '../../services'; -import { environment } from '../../../../environments/environment'; +import { environment } from '../../../../../environments/environment'; +import { UserService } from '../../../services'; +import { NotificationService } from '../../../../core/services'; -export class AuthServiceMock { +export class UserServiceMock { constructor() { } @@ -39,8 +40,8 @@ describe('SignUpComponent', () => { ], providers: [ { - provide: AuthService, - useClass: AuthServiceMock, + provide: UserService, + useClass: UserServiceMock, }, { provide: NotificationService, diff --git a/src/app/core/components/sign-up/sign-up.component.ts b/src/app/user/components/auth/sign-up/sign-up.component.ts similarity index 89% rename from src/app/core/components/sign-up/sign-up.component.ts rename to src/app/user/components/auth/sign-up/sign-up.component.ts index c57b62ad..170c1e76 100644 --- a/src/app/core/components/sign-up/sign-up.component.ts +++ b/src/app/user/components/auth/sign-up/sign-up.component.ts @@ -1,13 +1,15 @@ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms'; -import { environment } from '../../../../environments/environment'; -import { LegacyAccount, Notification } from '../../models'; -import { AuthService, NotificationService } from '../../services'; -import { HttpErrorResponse } from '@angular/common/http'; +import { NotificationService } from '../../../../core/services'; +import { Notification } from '../../../../core/models'; +import { UserService } from '../../../services'; import { Router } from '@angular/router'; -import { AppRoutes } from '../../../routes'; -import { ValidatePassword } from '../../validators/password.validator'; -import { messages } from '../../../../i18n/notification-messages/notification-messages'; +import { environment } from '../../../../../environments/environment'; +import { ValidatePassword } from '../../../validators/password.validator'; +import { LegacyAccount } from '../../../models'; +import { messages } from '../../../../../i18n/notification-messages/notification-messages'; +import { AppRoutes } from '../../../../routes'; +import { HttpErrorResponse } from '@angular/common/http'; @Component({ selector: 'app-sign-up', @@ -20,7 +22,7 @@ export class SignUpComponent implements OnInit { constructor( private _fb: FormBuilder, - private _authService: AuthService, + private _userService: UserService, private _notificationService: NotificationService, private _router: Router, ) { @@ -121,7 +123,7 @@ export class SignUpComponent implements OnInit { if (!this.formIsInvalid && !this.formDisabled) { const account = new LegacyAccount(this.form.value); this.form.disable(); - this._authService.createAccount(account).subscribe( + this._userService.createAccount(account).subscribe( (res) => { this.form.enable(); this._notificationService.notify( @@ -131,7 +133,7 @@ export class SignUpComponent implements OnInit { }, ); this.form.reset(); - this._authService.login({ username: account.username, password: account.password }).subscribe( + this._userService.login({ username: account.username, password: account.password }).subscribe( (res) => { this._router.navigate(['/', AppRoutes.home.uri]); }, diff --git a/src/app/user/components/index.ts b/src/app/user/components/index.ts index 51185439..4974dae7 100644 --- a/src/app/user/components/index.ts +++ b/src/app/user/components/index.ts @@ -1,10 +1,14 @@ import { UserServicesComponent } from '../components/user-services/user-services.component'; -import { UserAccountComponent } from '../components/user-account/user-account.component'; +import { UserProfilComponent } from '../components/user-profil/user-profil.component'; +import { LoginComponent } from './auth/login/login.component'; +import { SignUpComponent } from './auth/sign-up/sign-up.component'; -export { UserServicesComponent, UserAccountComponent }; +export { UserServicesComponent, UserProfilComponent, LoginComponent, SignUpComponent }; // tslint:disable-next-line:variable-name export const UserComponents = [ UserServicesComponent, - UserAccountComponent, + UserProfilComponent, + LoginComponent, + SignUpComponent, ]; diff --git a/src/app/user/components/user-account/user-account.component.spec.ts b/src/app/user/components/user-account/user-account.component.spec.ts deleted file mode 100644 index 70586ec9..00000000 --- a/src/app/user/components/user-account/user-account.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { UserAccountComponent } from './user-account.component'; - -describe('UserAccountComponent', () => { - let component: UserAccountComponent; - let fixture: ComponentFixture<UserAccountComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ UserAccountComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(UserAccountComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/user/components/user-profil/user-profi.component.spec.ts b/src/app/user/components/user-profil/user-profi.component.spec.ts new file mode 100644 index 00000000..d321f238 --- /dev/null +++ b/src/app/user/components/user-profil/user-profi.component.spec.ts @@ -0,0 +1,456 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ReactiveFormsModule, AbstractControl } from '@angular/forms'; +import { UserService } from '../../services'; +import { NotificationService } from '../../../core/services'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import { environment } from '../../../../environments/environment'; +import { UserProfilComponent } from './user-profil.component'; + +export class UserServiceMock { + + constructor() { } + + getUserInfo() { + return of(); + } + +} + +export class NotificationServiceMock { + + constructor() { } + +} + +describe('UserProfilComponent', () => { + let component: UserProfilComponent; + let fixture: ComponentFixture<UserProfilComponent>; + let firstName: AbstractControl; + let lastName: AbstractControl; + let newPassword: AbstractControl; + let newPasswordConfirmation: AbstractControl; + let oldPassword: AbstractControl; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [UserProfilComponent], + imports: [ + ReactiveFormsModule, + RouterTestingModule, + ], + providers: [ + { + provide: UserService, + useClass: UserServiceMock, + }, + { + provide: NotificationService, + useClass: NotificationServiceMock, + }, + ], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserProfilComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + firstName = component.userInfoUpdateForm.get('firstName'); + lastName = component.userInfoUpdateForm.get('lastName'); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + // tslint:disable-next-line:max-line-length + describe('User Info update Form validation (when testing if control is invalid we are also testing if the form is invalid)', () => { + it('form invalid when empty', () => { + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + }); + + describe('FirstName control validation', () => { + beforeEach(() => { + firstName.reset(); + }); + + it('firstName control is valid with lowercase and uppercase letter', () => { + // Given + const value = 'John Doe'; + // When + firstName.setValue(value); + // Then + expect(firstName.valid).toBeTruthy(); + }); + + it('firstName control is valid with letter and space', () => { + // Given + const value = 'John Doe'; + // When + firstName.setValue(value); + // Then + expect(firstName.valid).toBeTruthy(); + }); + + it('firstName control is valid with letter and dash', () => { + // Given + const value = 'Jean-jacques'; + // When + firstName.setValue(value); + // Then + expect(firstName.valid).toBeTruthy(); + }); + + it('firstName control is valid with letter and apostrophe', () => { + // Given + const value = 'De\'Andre'; + // When + firstName.setValue(value); + // Then + expect(firstName.valid).toBeTruthy(); + }); + + it('firstName control accept any of those characters', () => { + // Given + let specialChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + specialChars += 'abcdefghijklmnopqrstuvwxyz'; + specialChars += 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝ'; + specialChars += 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ'; + // When + firstName.setValue(specialChars); + // Then + expect(firstName.valid).toBeTruthy(); + }); + + it('firstName control is invalid if first letter is lowercased, has error "pattern"', () => { + // Given + const value = 'david'; + // When + firstName.setValue(value); + const errors = firstName.errors || {}; + // Then + expect(firstName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['pattern']).toBeTruthy(); + }); + + it('firstName control is invalid if contains numbers', () => { + // Given + const value = '0123456789'; + // When + firstName.setValue(value); + const errors = firstName.errors || {}; + // Then + expect(firstName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['pattern']).toBeTruthy(); + }); + + it('firstName control is invalid if contains special characters', () => { + // Given + const value = '"*$^!qjsdk+°)'; + // When + firstName.setValue(value); + const errors = firstName.errors || {}; + // Then + expect(firstName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['pattern']).toBeTruthy(); + }); + + it('firstName control is invalid if the value is empty', () => { + // Given + const value = ''; + // When + firstName.setValue(value); + const errors = firstName.errors || {}; + // Then + expect(firstName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['required']).toBeTruthy(); + }); + }); + + describe('LastName control validation', () => { + beforeEach(() => { + lastName.reset(); + }); + + it('lastName control is valid with lowercase and uppercase letter', () => { + // Given + const value = 'John Doe'; + // When + lastName.setValue(value); + // Then + expect(lastName.valid).toBeTruthy(); + }); + + it('lastName control is valid with letter and space', () => { + // Given + const value = 'John Doe'; + // When + lastName.setValue(value); + // Then + expect(lastName.valid).toBeTruthy(); + }); + + it('lastName control is valid with letter and dash', () => { + // Given + const value = 'Jean-jacques'; + // When + lastName.setValue(value); + // Then + expect(lastName.valid).toBeTruthy(); + }); + + it('lastName control is valid with letter and apostrophe', () => { + // Given + const value = 'De\'Andre'; + // When + lastName.setValue(value); + // Then + expect(lastName.valid).toBeTruthy(); + }); + + it('lastName control accept any of those characters', () => { + // Given + let specialChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + specialChars += 'abcdefghijklmnopqrstuvwxyz'; + specialChars += 'ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝ'; + specialChars += 'àáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ'; + // When + lastName.setValue(specialChars); + // Then + expect(lastName.valid).toBeTruthy(); + }); + + it('lastName control is invalid if first letter is lowercased, has error "pattern"', () => { + // Given + const value = 'david'; + // When + lastName.setValue(value); + const errors = lastName.errors || {}; + // Then + expect(lastName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['pattern']).toBeTruthy(); + }); + + it('lastName control is invalid if contains numbers', () => { + // Given + const value = '0123456789'; + // When + lastName.setValue(value); + const errors = lastName.errors || {}; + // Then + expect(lastName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['pattern']).toBeTruthy(); + }); + + it('lastName control is invalid if contains special characters', () => { + // Given + const value = '"*$^!qjsdk+°)'; + // When + lastName.setValue(value); + const errors = lastName.errors || {}; + // Then + expect(lastName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['pattern']).toBeTruthy(); + }); + + it('lastName control is invalid if the value is empty', () => { + // Given + const value = ''; + // When + lastName.setValue(value); + const errors = lastName.errors || {}; + // Then + expect(lastName.valid).toBeFalsy(); + expect(component.userInfoUpdateForm.valid).toBeFalsy(); + expect(errors['required']).toBeTruthy(); + }); + }); + }); + + // tslint:disable-next-line:max-line-length + describe('User password update Form validation (when testing if control is invalid we are also testing if the form is invalid)', () => { + beforeEach(() => { + fixture = TestBed.createComponent(UserProfilComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + newPassword = component.passwordUpdateForm.get('newPassword'); + newPasswordConfirmation = component.passwordUpdateForm.get('newPasswordConfirmation'); + oldPassword = component.passwordUpdateForm.get('oldPassword'); + }); + + describe('newPassword control validation', () => { + beforeEach(() => { + newPassword.reset(); + }); + + it('newPassword control is invalid if the value is empty', () => { + // Given + const value = ''; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(newPassword.valid).toBeFalsy(); + expect(component.passwordUpdateForm.valid).toBeFalsy(); + expect(errors['required']).toBeTruthy(); + }); + + it('newPassword control shouldn\'t have required error if the value is set', () => { + // Given + const value = 'dsfsfdf'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['required']).toBeFalsy(); + }); + + it(`password control is invalid if its length is inferior to ${environment.passwordMinLength}`, () => { + // Given + const value = 'abc'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(newPassword.valid).toBeFalsy(); + expect(component.passwordUpdateForm.valid).toBeFalsy(); + expect(errors['minlength']).toBeTruthy(); + }); + + // tslint:disable-next-line:max-line-length + it(`password control has not minLength error if its length is superior to ${environment.passwordMinLength}`, () => { + // Given + const value = 'abcqqsdsqdqsdsqdqsdsqsqsdsqddsqsdsqdsq'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['minlength']).toBeFalsy(); + }); + + // tslint:disable-next-line:max-line-length + it('newPassword control has \'missingSpecialCharacters\' error if there is not at least one special character', () => { + // Given + const value = 'abcq'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['missingSpecialCharacters']).toBeTruthy(); + }); + + // tslint:disable-next-line:max-line-length + it('newPassword control has \'missingUppercasedLetter\' error if there is not at least one uppercased letter', () => { + // Given + const value = 'abcq'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['missingUppercasedLetter']).toBeTruthy(); + }); + + // tslint:disable-next-line:max-line-length + it('newPassword control has \'missingLowercasedLetter\' error if there is not at least one lowercassed letter', () => { + // Given + const value = '1234TYARKS'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['missingLowercasedLetter']).toBeTruthy(); + }); + + // tslint:disable-next-line:max-line-length + it('newPassword control has \'missingNumber\' error if there is not at least one digit', () => { + // Given + const value = 'aaazezfrez'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['missingNumber']).toBeTruthy(); + }); + + // tslint:disable-next-line:max-line-length + it('newPassword control has \'forbiddenWord\' error if the password contains one of the forbidden words', () => { + // Given + const value = 'sqddsqgrandlyondsqsqdsq'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['forbiddenWord']).toBeTruthy(); + }); + + // tslint:disable-next-line:max-line-length + it('newPassword control has no errors from validatePassword is respecting all the rules', () => { + // Given + const value = 'azeAZE123!'; + // When + newPassword.setValue(value); + const errors = newPassword.errors || {}; + // Then + expect(errors['missingSpecialCharacters']).toBeFalsy(); + expect(errors['missingUppercasedLetter']).toBeFalsy(); + expect(errors['missingLowercasedLetter']).toBeFalsy(); + expect(errors['missingNumber']).toBeFalsy(); + expect(errors['forbiddenWord']).toBeFalsy(); + }); + + it('newPassword control should be valid when respecting all the validator', () => { + // Given + const value = 'azeAZE123!'; + // When + newPassword.setValue(value); + // Then + expect(newPassword.valid).toBeTruthy(); + }); + }); + + describe('passwordConfirmation control validation', () => { + beforeEach(() => { + newPasswordConfirmation.reset(); + }); + + it('passwordConfirmation control is invalid if the value is empty', () => { + // Given + const value = ''; + // When + newPasswordConfirmation.setValue(value); + const errors = newPasswordConfirmation.errors || {}; + // Then + expect(newPasswordConfirmation.valid).toBeFalsy(); + expect(component.passwordUpdateForm.valid).toBeFalsy(); + expect(errors['required']).toBeTruthy(); + }); + }); + + describe('oldPassword control validation', () => { + beforeEach(() => { + oldPassword.reset(); + }); + + it('oldPassword control is invalid if the value is empty', () => { + // Given + const value = ''; + // When + oldPassword.setValue(value); + const errors = oldPassword.errors || {}; + // Then + expect(oldPassword.valid).toBeFalsy(); + expect(component.passwordUpdateForm.valid).toBeFalsy(); + expect(errors['required']).toBeTruthy(); + }); + }); + }); +}); diff --git a/src/app/user/components/user-account/user-account.component.html b/src/app/user/components/user-profil/user-profil.component.html similarity index 95% rename from src/app/user/components/user-account/user-account.component.html rename to src/app/user/components/user-profil/user-profil.component.html index 135a7f36..2944f72a 100644 --- a/src/app/user/components/user-account/user-account.component.html +++ b/src/app/user/components/user-profil/user-profil.component.html @@ -1,4 +1,4 @@ -<h1 class="has-text-centered" i18n="@@userAccount.updateProfil">Update your profil</h1> +<h1 class="has-text-centered" i18n="@@userProfil.updateProfil">Update your profil</h1> <div class="user-account-forms-container"> <form [formGroup]="userInfoUpdateForm" (ngSubmit)="updateUserInfo()"> @@ -125,7 +125,7 @@ <div class="column is-12 has-text-right"> <div class="field"> <div> - <button type="submit" class="button button-gl" [ngClass]="{'is-loading': userInfoUpdateForm.disabled}" i18n="@@userAccount.updateProfil" + <button type="submit" class="button button-gl" [ngClass]="{'is-loading': userInfoUpdateForm.disabled}" i18n="@@userProfil.updateProfil" [disabled]="userInfoUpdateFormIsInvalid || userInfoUpdateFormDisabled || !userInfoUpdateForm.dirty">Update your profil</button> </div> @@ -140,7 +140,7 @@ <div class="columns is-multiline"> <div class="column is-12-mobile is-6-tablet"> <div class="field"> - <label class="label" for="password"><span i18n="@@userAccount.newPassword">New password</span><span class="required-field">*</span></label> + <label class="label" for="password"><span i18n="@@userProfil.newPassword">New password</span><span class="required-field">*</span></label> <p class="control has-icons-right"> <input id="newPassword" class="input" type="password" formControlName="newPassword" [ngClass]="{'is-danger': passwordFormFieldIsInvalid('newPassword'), 'is-success': passwordFormFieldIsValid('newPassword')}"> <span class="icon is-small is-right has-text-success" *ngIf="passwordFormFieldIsValid('newPassword')"> @@ -185,7 +185,7 @@ <div class="column is-12-mobile is-6-tablet"> <div class="field"> - <label class="label" for="newPasswordConfirmation"><span i18n="@@userAccount.newPasswordConfirmation">New + <label class="label" for="newPasswordConfirmation"><span i18n="@@userProfil.newPasswordConfirmation">New password confirmation</span><span class="required-field">*</span></label> <p class="control has-icons-right"> <input blockCopyPaste id="newPasswordConfirmation" class="input" type="password" formControlName="newPasswordConfirmation" @@ -213,7 +213,7 @@ <div class="column is-12-mobile is-6-tablet"> <div class="field"> - <label class="label" for="oldPassword"><span i18n="@@userAccount.oldPassword">Old password</span><span class="required-field">*</span></label> + <label class="label" for="oldPassword"><span i18n="@@userProfil.oldPassword">Old password</span><span class="required-field">*</span></label> <p class="control has-icons-right"> <input blockCopyPaste id="oldPassword" class="input" type="password" formControlName="oldPassword" [ngClass]="{'is-danger': passwordFormFieldIsInvalid('oldPassword'), 'is-success': passwordFormFieldIsValid('oldPassword')}"> @@ -226,11 +226,11 @@ </p> <div class="form-incorrect-field-message" *ngIf="passwordFormFieldIsInvalid('oldPassword')"> - <div *ngIf="oldPassword.errors['required']" i18n="@@userAccount.errors.missingOldPassword"> + <div *ngIf="oldPassword.errors['required']" i18n="@@userProfil.errors.missingOldPassword"> You must enter your old password. </div> - <div *ngIf="oldPassword.errors['wrongPassword']" i18n="@@userAccount.errors.wrongPassword"> + <div *ngIf="oldPassword.errors['wrongPassword']" i18n="@@userProfil.errors.wrongPassword"> Make sure to enter the right password. </div> </div> @@ -240,7 +240,7 @@ <div class="field"> <div> <button type="submit" class="button button-gl" [ngClass]="{'is-loading': passwordUpdateForm.disabled}" - [disabled]="passwordUpdateFormIsInvalid || passwordUpdateFormDisabled" i18n="@@userAccount.updatePassword"> + [disabled]="passwordUpdateFormIsInvalid || passwordUpdateFormDisabled" i18n="@@userProfil.updatePassword"> Update your password </button> </div> diff --git a/src/app/user/components/user-account/user-account.component.scss b/src/app/user/components/user-profil/user-profil.component.scss similarity index 100% rename from src/app/user/components/user-account/user-account.component.scss rename to src/app/user/components/user-profil/user-profil.component.scss diff --git a/src/app/user/components/user-account/user-account.component.ts b/src/app/user/components/user-profil/user-profil.component.ts similarity index 82% rename from src/app/user/components/user-account/user-account.component.ts rename to src/app/user/components/user-profil/user-profil.component.ts index 1b4f4a68..28679500 100644 --- a/src/app/user/components/user-account/user-account.component.ts +++ b/src/app/user/components/user-profil/user-profil.component.ts @@ -1,29 +1,27 @@ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/forms'; import { environment } from '../../../../environments/environment'; -import { ValidatePassword } from '../../../core/validators/password.validator'; +import { ValidatePassword } from '../../validators/password.validator'; import { UserService } from '../../services'; -import { IUserInfo, PasswordUpdateForm } from '../../models'; +import { PasswordUpdateForm } from '../../models'; import { messages } from '../../../../i18n/notification-messages/notification-messages'; -import { NotificationService, AuthService } from '../../../core/services'; +import { NotificationService } from '../../../core/services'; import { Router } from '@angular/router'; import { AppRoutes } from '../../../routes'; @Component({ - selector: 'app-user-account', - templateUrl: './user-account.component.html', - styleUrls: ['./user-account.component.scss'], + selector: 'app-user-profil', + templateUrl: './user-profil.component.html', + styleUrls: ['./user-profil.component.scss'], }) -export class UserAccountComponent implements OnInit { +export class UserProfilComponent implements OnInit { passwordUpdateForm: FormGroup; userInfoUpdateForm: FormGroup; - // userInfo: IUserInfo; constructor( private _fb: FormBuilder, private _userService: UserService, - private _authService: AuthService, private _notificationService: NotificationService, private _router: Router, ) { @@ -36,29 +34,39 @@ export class UserAccountComponent implements OnInit { newPasswordConfirmation: ['', [Validators.required]], oldPassword: ['', [Validators.required]], }); + + this.userInfoUpdateForm = this._fb.group({ + email: [], + firstName: [, [ + Validators.required, + Validators.pattern('([A-ZÀ-ÖØ-Ý][a-zà-öø-ýÿA-ZÀ-ÖØ-Ý]*)([ \'-][a-zA-ZÀ-ÖØ-öø-ýÿ]*)*'), + ]], + lastName: [, [ + Validators.required, + Validators.pattern('([A-ZÀ-ÖØ-Ý][a-zà-öø-ýÿA-ZÀ-ÖØ-Ý]*)([ \'-][a-zA-ZÀ-ÖØ-öø-ýÿ]*)*'), + ]], + acceptMessages: [], + entreprise: [], + address: [], + zipcode: [], + city: [], + country: [], + }); } ngOnInit() { this._userService.getUserInfo().subscribe( (info) => { - // this.userInfo = info; - - this.userInfoUpdateForm = this._fb.group({ - email: [info.email], - firstName: [info.firstName, [ - Validators.required, - Validators.pattern('([A-ZÀ-ÖØ-Ý][a-zà-öø-ýÿA-ZÀ-ÖØ-Ý]*)([ \'-][a-zA-ZÀ-ÖØ-öø-ýÿ]*)*'), - ]], - lastName: [info.lastName, [ - Validators.required, - Validators.pattern('([A-ZÀ-ÖØ-Ý][a-zà-öø-ýÿA-ZÀ-ÖØ-Ý]*)([ \'-][a-zA-ZÀ-ÖØ-öø-ýÿ]*)*'), - ]], - acceptMessages: [info.acceptMessages], - entreprise: [info.entreprise], - address: [info.address], - zipcode: [info.zipcode], - city: [info.city], - country: [info.country], + this.userInfoUpdateForm.reset({ + email: info.email, + firstName: info.firstName, + lastName: info.lastName, + acceptMessages: info.acceptMessages, + entreprise: info.entreprise, + address: info.address, + zipcode: info.zipcode, + city: info.city, + country: info.country, }); }, (err) => { @@ -158,17 +166,16 @@ export class UserAccountComponent implements OnInit { ); // If password updated with success // Relog the user with the new credentials - const loginForm = { username: this._authService.user.username, password: this.newPassword.value }; - this._authService.login(loginForm).subscribe( + const loginForm = { username: this._userService.user.username, password: this.newPassword.value }; + this._userService.login(loginForm).subscribe( // If loggedin with success reset the form // Else redirect the user to the login page (res) => { this.passwordUpdateForm.reset(); }, (err) => { - this._authService.resetAuth(); + this._userService.resetAuth(); this._router.navigate(['/', AppRoutes.signin.uri]); - }, ); }, diff --git a/src/app/user/components/user-services/user-services.component.ts b/src/app/user/components/user-services/user-services.component.ts index dc837d0d..51baf972 100644 --- a/src/app/user/components/user-services/user-services.component.ts +++ b/src/app/user/components/user-services/user-services.component.ts @@ -135,7 +135,7 @@ export class UserServicesComponent implements OnInit { const resource = array.find(e => e.datasetId === elementId); if (resource) { let bool = false; - const unselectedMode = resource.services.find(e => e.selected === false); + const unselectedMode = resource.services.find(e => !e.selected); if (unselectedMode) { bool = true; @@ -152,7 +152,7 @@ export class UserServicesComponent implements OnInit { const resource = array.find(e => e.datasetId === datasetId); if (resource) { - const unselectedMode = resource.services.find(e => e.selected === false); + const unselectedMode = resource.services.find(e => !e.selected); if (unselectedMode) { res = false; @@ -167,8 +167,8 @@ export class UserServicesComponent implements OnInit { const resource = array.find(e => e.datasetId === datasetId); if (resource) { - const unselectedService = resource.services.find(e => e.selected === false); - const selectedService = resource.services.find(e => e.selected === true); + const unselectedService = resource.services.find(e => !e.selected); + const selectedService = resource.services.find(e => e.selected); if (unselectedService && selectedService) { res = true; @@ -180,9 +180,9 @@ export class UserServicesComponent implements OnInit { selectedResources(array: Resource[]) { return array.filter((e) => { - return e.services.findIndex(m => m.selected === true) !== -1 ? true : false; + return e.services.findIndex(m => m.selected) !== -1 ? true : false; }).map((e) => { - const servicesArray = e.services.filter(m => m.selected === true); + const servicesArray = e.services.filter(m => m.selected); const r = new Resource(e); r.services = servicesArray; return r; diff --git a/src/app/core/guards/authenticated.guard.ts b/src/app/user/guards/authenticated.guard.ts similarity index 70% rename from src/app/core/guards/authenticated.guard.ts rename to src/app/user/guards/authenticated.guard.ts index d871701c..54bf5e58 100644 --- a/src/app/core/guards/authenticated.guard.ts +++ b/src/app/user/guards/authenticated.guard.ts @@ -1,15 +1,15 @@ import { Injectable } from '@angular/core'; import { Router, CanActivate } from '@angular/router'; -import { AuthService } from '../services'; import { AppRoutes } from '../../routes'; +import { UserService } from '../services'; @Injectable() export class AuthenticatedGuard implements CanActivate { - constructor(public _authService: AuthService, public router: Router) {} + constructor(public _userService: UserService, public router: Router) {} canActivate(): boolean { - if (!this._authService.userIsSignedIn) { + if (!this._userService.userIsSignedIn) { this.router.navigate(['/', AppRoutes.signin.uri]); return false; } diff --git a/src/app/core/guards/index.ts b/src/app/user/guards/index.ts similarity index 82% rename from src/app/core/guards/index.ts rename to src/app/user/guards/index.ts index ffaceec9..1951c3f0 100644 --- a/src/app/core/guards/index.ts +++ b/src/app/user/guards/index.ts @@ -1,6 +1,6 @@ import { AuthenticatedGuard } from './authenticated.guard'; // tslint:disable-next-line:variable-name -export const CoreGuards = [ +export const UserGuards = [ AuthenticatedGuard, ]; diff --git a/src/app/core/interceptors/auth-interceptor.ts b/src/app/user/interceptors/auth-interceptor.ts similarity index 100% rename from src/app/core/interceptors/auth-interceptor.ts rename to src/app/user/interceptors/auth-interceptor.ts diff --git a/src/app/user/models/index.ts b/src/app/user/models/index.ts index 50d2aeb2..ec7623d3 100644 --- a/src/app/user/models/index.ts +++ b/src/app/user/models/index.ts @@ -1,7 +1,8 @@ import { IRestrictedAccessDataset, IService, IResource, Resource, ServiceAccess, Service } from './user-services.model'; -import { IUserInfo, PasswordUpdateForm } from './user.model'; +import { User, ILoginResponse, ICreateAccountForm, LegacyAccount, IUserInfo, PasswordUpdateForm } from './user.model'; export { IRestrictedAccessDataset, IService, IResource, Resource, ServiceAccess, Service, IUserInfo, PasswordUpdateForm, + User, ILoginResponse, ICreateAccountForm, LegacyAccount, }; diff --git a/src/app/user/models/user.model.ts b/src/app/user/models/user.model.ts index aaad49d7..1cfabd2d 100644 --- a/src/app/user/models/user.model.ts +++ b/src/app/user/models/user.model.ts @@ -1,3 +1,70 @@ +export class User { + id: number; + firstName: string; + lastName: string; + email: string; + username: string; + + // payload is the decrypted payload of the JWT token + constructor(payload) { + if (payload) { + this.id = payload.id; + this.firstName = payload.firstName; + this.lastName = payload.lastName; + this.email = payload.email; + this.username = payload.username; + } + } +} + +export interface ILoginResponse { + token: string; +} + +export interface ICreateAccountForm { + firstName: string; + lastName: string; + email: string; + emailConfirmation: string; + password: string; + passwordConfirmation: string; + acceptMessages: boolean; + entreprise: string; + address: string; + zipcode: string; + city: string; + country: string; + cgu: boolean; +} + +export class LegacyAccount { + firstName: string; + lastName: string; + username: string; + email: string; + password: string; + acceptMessages: boolean; + entreprise: string; + address: string; + zipcode: string; + city: string; + country: string; + + constructor(form: ICreateAccountForm) { + this.firstName = form.firstName; + this.lastName = form.lastName; + this.username = form.email; + this.email = form.email; + this.password = form.password; + this.acceptMessages = form.acceptMessages; + this.entreprise = form.entreprise; + this.address = form.address; + this.zipcode = form.zipcode; + this.city = form.city; + this.country = form.country; + } +} + export interface IUserInfo { username: string; lastName: string; diff --git a/src/app/user/services/user.service.ts b/src/app/user/services/user.service.ts index 5d024cbc..164c1a68 100644 --- a/src/app/user/services/user.service.ts +++ b/src/app/user/services/user.service.ts @@ -1,17 +1,83 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; -import { IUserInfo, PasswordUpdateForm } from '../models'; +import { IUserInfo, PasswordUpdateForm, User, ILoginResponse, LegacyAccount } from '../models'; import { environment } from '../../../environments/environment'; import { map } from 'rxjs/operators'; +import * as JwtDecode from 'jwt-decode'; @Injectable() export class UserService { + private _user: User = null; + constructor( private _http: HttpClient, ) { } + // Function and helpers allowing the management of the user session (jwt), info... + private setSession(authResult): boolean { + let success = false; + if (authResult && authResult.token) { + localStorage.setItem('token', authResult.token); + this.setUserInfo(); + success = true; + } else { + this.resetAuth(); + } + + return success; + } + + setUserInfo() { + const token = localStorage.getItem('token'); + try { + const payload = JwtDecode(token); + this._user = new User(payload); + } catch (error) { + } + } + + resetAuth() { + localStorage.removeItem('token'); + this._user = null; + } + + get user() { + return this._user; + } + + get userIsSignedIn() { + return this._user != null; + } + + // HTTP Calls + login(loginForm): Observable<boolean> { + return this._http.post<ILoginResponse>(environment.legacyLoginEndpoint, loginForm).pipe( + map( + (res) => { + return this.setSession(res); + }, + (err) => { + throw err; + }, + ), + ); + } + + createAccount(legacyAccount: LegacyAccount): Observable<boolean> { + return this._http.post<any>(`${environment.middlewareLegacyAuth}user/`, legacyAccount).pipe( + map( + (res) => { + return true; + }, + (err) => { + throw err; + }, + ), + ); + } + getUserInfo(): Observable<IUserInfo> { return this._http.get<IUserInfo>(`${environment.middlewareLegacyAuth}user`).pipe( map( diff --git a/src/app/user/user-routing.module.ts b/src/app/user/user-routing.module.ts index 5cc0adbd..3d105ed2 100644 --- a/src/app/user/user-routing.module.ts +++ b/src/app/user/user-routing.module.ts @@ -1,19 +1,32 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AppRoutes } from '../routes'; -import { AuthenticatedGuard } from '../core/guards/authenticated.guard'; -import { UserServicesComponent } from './components/user-services/user-services.component'; -import { UserAccountComponent } from './components'; +import { AuthenticatedGuard } from './guards/authenticated.guard'; +import { UserProfilComponent, LoginComponent, SignUpComponent, UserServicesComponent } from './components'; export const routes: Routes = [ + { + path: AppRoutes.signin.uri, + component: LoginComponent, + data: { + title: AppRoutes.signin.title, + }, + }, + { + path: AppRoutes.signup.uri, + component: SignUpComponent, + data: { + title: AppRoutes.signup.title, + }, + }, { path: AppRoutes.userServices.uri, component: UserServicesComponent, canActivate: [AuthenticatedGuard], }, { - path: AppRoutes.userAccount.uri, - component: UserAccountComponent, + path: AppRoutes.userProfil.uri, + component: UserProfilComponent, canActivate: [AuthenticatedGuard], }, ]; diff --git a/src/app/user/user.module.ts b/src/app/user/user.module.ts index 19766bfc..7ea52171 100644 --- a/src/app/user/user.module.ts +++ b/src/app/user/user.module.ts @@ -4,6 +4,9 @@ import { UserRoutingModule } from './user-routing.module'; import { UserServices } from './services'; import { UserComponents } from './components'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { AuthInterceptor } from './interceptors/auth-interceptor'; +import { UserGuards } from './guards'; @NgModule({ imports: [ @@ -13,7 +16,13 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; ReactiveFormsModule, ], providers: [ - UserServices, + ...UserGuards, + ...UserServices, + { + provide: HTTP_INTERCEPTORS, + useClass: AuthInterceptor, + multi: true, + }, ], declarations: [...UserComponents], }) diff --git a/src/app/core/validators/password.validator.ts b/src/app/user/validators/password.validator.ts similarity index 100% rename from src/app/core/validators/password.validator.ts rename to src/app/user/validators/password.validator.ts diff --git a/src/i18n/messages.en.xlf b/src/i18n/messages.en.xlf index b223c6d7..6d012c0b 100644 --- a/src/i18n/messages.en.xlf +++ b/src/i18n/messages.en.xlf @@ -42,9 +42,9 @@ <source>My services</source> <target>My services</target> </trans-unit> - <trans-unit id="header.userAccount" datatype="html"> - <source>My account</source> - <target>My account</target> + <trans-unit id="header.userProfil" datatype="html"> + <source>My profil</source> + <target>My profil</target> </trans-unit> <trans-unit id="footer.rss" datatype="html"> <source>RSS Feed (new window)</source> @@ -609,31 +609,31 @@ <source>Remove access</source> <target>Remove access</target> </trans-unit> - <trans-unit id="userAccount.updateProfil" datatype="html"> + <trans-unit id="userProfil.updateProfil" datatype="html"> <source>Update your profil</source> <target>Update your profil</target> </trans-unit> - <trans-unit id="userAccount.newPassword" datatype="html"> + <trans-unit id="userProfil.newPassword" datatype="html"> <source>New password</source> <target>New password</target> </trans-unit> - <trans-unit id="userAccount.newPasswordConfirmation" datatype="html"> + <trans-unit id="userProfil.newPasswordConfirmation" datatype="html"> <source>New password confirmation</source> <target>New password confirmation</target> </trans-unit> - <trans-unit id="userAccount.oldPassword" datatype="html"> + <trans-unit id="userProfil.oldPassword" datatype="html"> <source>Old password</source> <target>Old password</target> </trans-unit> - <trans-unit id="userAccount.errors.missingOldPassword" datatype="html"> + <trans-unit id="userProfil.errors.missingOldPassword" datatype="html"> <source>You must enter your old password.</source> <target>You must enter your old password.</target> </trans-unit> - <trans-unit id="userAccount.errors.wrongPassword" datatype="html"> + <trans-unit id="userProfil.errors.wrongPassword" datatype="html"> <source>Make sure to enter the right password.</source> <target>Make sure to enter the right password.</target> </trans-unit> - <trans-unit id="userAccount.updatePassword" datatype="html"> + <trans-unit id="userProfil.updatePassword" datatype="html"> <source>Update your password</source> <target>Update your password</target> </trans-unit> diff --git a/src/i18n/messages.fr.xlf b/src/i18n/messages.fr.xlf index e5b9f33b..56334fd8 100644 --- a/src/i18n/messages.fr.xlf +++ b/src/i18n/messages.fr.xlf @@ -42,9 +42,9 @@ <source>My services</source> <target>Mes services</target> </trans-unit> - <trans-unit id="header.userAccount" datatype="html"> - <source>My account</source> - <target>Mon compte</target> + <trans-unit id="header.userProfil" datatype="html"> + <source>My profil</source> + <target>Mon profil</target> </trans-unit> <trans-unit id="footer.rss" datatype="html"> <source>RSS Feed (new window)</source> @@ -617,31 +617,31 @@ <source>Remove access</source> <target>Supprimer les accès</target> </trans-unit> - <trans-unit id="userAccount.updateProfil" datatype="html"> + <trans-unit id="userProfil.updateProfil" datatype="html"> <source>Update your profil</source> <target>Modifier son profil</target> </trans-unit> - <trans-unit id="userAccount.newPassword" datatype="html"> + <trans-unit id="userProfil.newPassword" datatype="html"> <source>New password</source> <target>Nouveau mot de passe</target> </trans-unit> - <trans-unit id="userAccount.newPasswordConfirmation" datatype="html"> + <trans-unit id="userProfil.newPasswordConfirmation" datatype="html"> <source>New password confirmation</source> <target>Confirmation du nouveau mot de passe</target> </trans-unit> - <trans-unit id="userAccount.oldPassword" datatype="html"> + <trans-unit id="userProfil.oldPassword" datatype="html"> <source>Old password</source> <target>Ancien mot de passe</target> </trans-unit> - <trans-unit id="userAccount.errors.missingOldPassword" datatype="html"> + <trans-unit id="userProfil.errors.missingOldPassword" datatype="html"> <source>You must enter your old password.</source> <target>Veuillez saisir votre ancien mot de passe.</target> </trans-unit> - <trans-unit id="userAccount.errors.wrongPassword" datatype="html"> + <trans-unit id="userProfil.errors.wrongPassword" datatype="html"> <source>Make sure to enter the right password.</source> <target>Assurez vous d'avoir saisi le bon mot de passe.</target> </trans-unit> - <trans-unit id="userAccount.updatePassword" datatype="html"> + <trans-unit id="userProfil.updatePassword" datatype="html"> <source>Update your password</source> <target>Modifier son mot de passe</target> </trans-unit> -- GitLab