This is an automated email from the ASF dual-hosted git repository. ababiichuk pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/ambari.git
commit 9e07d7460642b36b0d3dd195920ab6aa2a0dd608 Author: Tobias Istvan <tobias.ist...@gmail.com> AuthorDate: Mon May 21 15:36:53 2018 +0200 [AMBARI-23897] Log Search UI: login with invalid password – no error message is displayed --- .../login-form/login-form.component.html | 2 +- .../login-form/login-form.component.spec.ts | 23 ++++---- .../components/login-form/login-form.component.ts | 61 ++++++++++++++++------ .../src/app/services/auth.service.ts | 31 ++++++++--- .../ambari-logsearch-web/src/assets/i18n/en.json | 2 + 5 files changed, 86 insertions(+), 33 deletions(-) diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.html index 8b9c957..9e8b2a5 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.html +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.html @@ -16,7 +16,7 @@ --> <div class="login-form well col-md-4 col-md-offset-4 col-sm-offset-4"> - <div class="alert alert-danger" *ngIf="isLoginAlertDisplayed" [innerHTML]="'authorization.error' | translate"></div> + <div class="alert alert-danger" *ngIf="isLoginAlertDisplayed" [innerHTML]="errorMessage"></div> <form #loginForm="ngForm" (ngSubmit)="login()"> <div class="form-group"> <label for="username">{{'authorization.name' | translate}}</label> diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts index 7e2f7a7..3ec55fd 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.spec.ts @@ -26,20 +26,23 @@ import {AuthService} from '@app/services/auth.service'; import {LoginFormComponent} from './login-form.component'; import {RouterTestingModule} from '@angular/router/testing'; +import {NotificationsService} from 'angular2-notifications'; +import {NotificationService} from '@app/modules/shared/services/notification.service'; describe('LoginFormComponent', () => { let component: LoginFormComponent; let fixture: ComponentFixture<LoginFormComponent>; const authMock = { - isError: false + isError: false, + isAuthorized: false }; - const httpClient = { - isAuthorized: true, - postFormData: () => { + + const AuthServiceMock = { + login: () => { return { - subscribe: (success: () => void, error: () => void) => { - authMock.isError ? error() : success(); + subscribe: (observer: (resp) => void, error: (resp) => void) => { + authMock.isAuthorized ? observer(authMock.isAuthorized) : error(authMock.isAuthorized); } }; } @@ -59,10 +62,11 @@ describe('LoginFormComponent', () => { providers: [ AppStateService, { - provide: HttpClientService, - useValue: httpClient + provide: AuthService, + useValue: AuthServiceMock }, - AuthService + NotificationsService, + NotificationService ] }) .compileComponents(); @@ -98,6 +102,7 @@ describe('LoginFormComponent', () => { describe(test.title, () => { beforeEach(() => { authMock.isError = test.isError; + authMock.isAuthorized = test.isAuthorized; component.login(); }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts index e3570c4..eee0b1b 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/login-form/login-form.component.ts @@ -16,19 +16,23 @@ * limitations under the License. */ -import {Component} from '@angular/core'; +import {Component, ViewChild, OnInit, OnDestroy} from '@angular/core'; import {Response} from '@angular/http'; +import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/finally'; +import {Subscription} from 'rxjs/Subscription'; import {AppStateService} from '@app/services/storage/app-state.service'; import {AuthService} from '@app/services/auth.service'; -import {Observable} from 'rxjs/Observable'; +import {NotificationService, NotificationType} from '@modules/shared/services/notification.service'; +import {TranslateService} from '@ngx-translate/core'; +import {FormGroup} from '@angular/forms'; @Component({ selector: 'login-form', templateUrl: './login-form.component.html', styleUrls: ['./login-form.component.less'] }) -export class LoginFormComponent { +export class LoginFormComponent implements OnInit, OnDestroy { username: string; @@ -38,24 +42,47 @@ export class LoginFormComponent { isLoginInProgress$: Observable<boolean> = this.appState.getParameter('isLoginInProgress'); - constructor(private authService: AuthService, private appState: AppStateService) {} + errorMessage: string; + + @ViewChild('loginForm') + loginForm: FormGroup; + + subscriptions: Subscription[] = []; + + constructor( + private authService: AuthService, + private appState: AppStateService, + private notificationService: NotificationService, + private translateService: TranslateService + ) {} + + ngOnInit(): void { + this.subscriptions.push( + this.loginForm.valueChanges.subscribe(this.onLoginFormChange) + ); + } + + ngOnDestroy(): void { + this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); + } - /** - * Handling the response from the login action. Actually the goal only to show or hide the login error alert. - * When it gets error response it shows. - * @param {Response} resp - */ - private onLoginError = (resp: Response): void => { - this.isLoginAlertDisplayed = true; + onLoginFormChange = (event) => { + this.isLoginAlertDisplayed = false; } - /** - * Handling the response from the login action. Actually the goal only to show or hide the login error alert. - * When it gets success response it hides. - * @param {Response} resp - */ - private onLoginSuccess = (resp: Response): void => { + private onLoginSuccess = (result: Boolean): void => { this.isLoginAlertDisplayed = false; + this.errorMessage = ''; + } + + private onLoginError = (resp: Boolean): void => { + Observable.combineLatest( + this.translateService.get('login.error.title'), + this.translateService.get('login.error.message') + ).first().subscribe(([title, message]: [string, string]) => { + this.errorMessage = message; + this.isLoginAlertDisplayed = true; + }); } login() { diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/auth.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/auth.service.ts index 87cdb41..a3ed9b8 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/auth.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/auth.service.ts @@ -25,6 +25,7 @@ import {HttpClientService} from '@app/services/http-client.service'; import {AppStateService} from '@app/services/storage/app-state.service'; import {Router} from '@angular/router'; import {Subscription} from 'rxjs/Subscription'; +import { Observer } from 'rxjs/Observer'; export const IS_AUTHORIZED_APP_STATE_KEY: string = 'isAuthorized'; export const IS_LOGIN_IN_PROGRESS_APP_STATE_KEY: string = 'isLoginInProgress'; @@ -75,30 +76,48 @@ export class AuthService { * @param {string} password * @returns {Observable<Response>} */ - login(username: string, password: string): Observable<Response> { + login(username: string, password: string): Observable<Boolean> { this.setLoginInProgressAppState(true); const response$ = this.httpClient.postFormData('login', { username: username, password: password - }); + }).share(); response$.subscribe( (resp: Response) => this.onLoginResponse(resp), (resp: Response) => this.onLoginError(resp) ); - return response$; + return response$.switchMap((resp: Response) => { + return Observable.create((observer: Observer<boolean>) => { + if (resp.ok) { + observer.next(resp.ok); + } else { + observer.error(resp); + } + observer.complete(); + }); + }); } /** * The single unique entry point to request a logout action * @returns {Observable<boolean | Error>} */ - logout(): Observable<Response> { - const response$ = this.httpClient.get('logout'); + logout(): Observable<Boolean> { + const response$ = this.httpClient.get('logout').share(); response$.subscribe( (resp: Response) => this.onLogoutResponse(resp), (resp: Response) => this.onLogoutError(resp) ); - return response$; + return response$.switchMap((resp: Response) => { + return Observable.create((observer) => { + if (resp.ok) { + observer.next(resp.ok); + } else { + observer.error(resp); + } + observer.complete(); + }); + }); } /** diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json index 00815f9..fec6991 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json +++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json @@ -219,6 +219,8 @@ "logIndexFilter.update.error": "Error at updating Log Index Filter for cluster <span class='cluster-name'>{{cluster}}</span>. {{message}}", "login.title": "Login", + "login.error.title": "Login error", + "login.error.message": "Unable to sign in. Invalid username/password combination.", "shipperConfiguration.title": "All Configuration", "shipperConfiguration.add": "Add", -- To stop receiving notification emails like this one, please contact ababiic...@apache.org.