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
The following commit(s) were added to refs/heads/trunk by this push: new 4f514e8 AMBARI-23537 Log Search UI: fix for Log Index Filter feature 4f514e8 is described below commit 4f514e8361e813ae50a1e9503753650b14d0b8b1 Author: Istvan Tobias <tobias.ist...@gmail.com> AuthorDate: Mon Apr 16 14:38:57 2018 +0200 AMBARI-23537 Log Search UI: fix for Log Index Filter feature --- .../action-menu/action-menu.component.spec.ts | 6 +- .../action-menu/action-menu.component.ts | 12 ++-- .../src/app/components/app.component.ts | 7 ++- .../log-index-filter.component.spec.ts | 6 +- .../log-index-filter/log-index-filter.component.ts | 37 +++++++----- .../timezone-picker.component.spec.ts | 6 +- .../src/app/modules/shared/notifications.less | 66 ++++++++++++++++++++++ .../shared/services/notification.service.ts | 17 +++++- .../src/app/modules/shared/variables.less | 7 +++ .../src/app/services/user-settings.service.spec.ts | 6 +- .../src/app/services/user-settings.service.ts | 60 ++++++++++++++------ .../ambari-logsearch-web/src/assets/i18n/en.json | 3 + .../src/mockdata/mock-data-get.ts | 2 +- 13 files changed, 190 insertions(+), 45 deletions(-) diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts index 63950f0..4ad7f44 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts @@ -49,6 +49,8 @@ import {RouterTestingModule} from '@angular/router/testing'; import {LogsStateService} from '@app/services/storage/logs-state.service'; import {RoutingUtilsService} from '@app/services/routing-utils.service'; import {LogsFilteringUtilsService} from '@app/services/logs-filtering-utils.service'; +import {NotificationsService} from 'angular2-notifications/src/notifications.service'; +import {NotificationService} from '@modules/shared/services/notification.service'; describe('ActionMenuComponent', () => { let component: ActionMenuComponent; @@ -104,7 +106,9 @@ describe('ActionMenuComponent', () => { ClusterSelectionService, RoutingUtilsService, LogsFilteringUtilsService, - LogsStateService + LogsStateService, + NotificationsService, + NotificationService ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts index 394bebc..ad38711 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts @@ -30,6 +30,12 @@ import {ListItem} from '@app/classes/list-item'; }) export class ActionMenuComponent { + isLogIndexFilterDisplayed: boolean = false; + + settingsForm: FormGroup = this.settings.settingsFormGroup; + + isModalSubmitDisabled: boolean = true; + constructor( private logsContainer: LogsContainerService, private historyManager: HistoryManagerService, private settings: UserSettingsService @@ -52,12 +58,6 @@ export class ActionMenuComponent { return this.logsContainer.captureSeconds; } - isLogIndexFilterDisplayed: boolean = false; - - settingsForm: FormGroup = this.settings.settingsFormGroup; - - isModalSubmitDisabled: boolean = true; - setModalSubmitDisabled(isDisabled: boolean): void { this.isModalSubmitDisabled = isDisabled; } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts index e9445c2..62f95bb 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/app.component.ts @@ -20,11 +20,12 @@ import {Component} from '@angular/core'; import {AppStateService} from '@app/services/storage/app-state.service'; import {Observable} from 'rxjs/Observable'; import {Options} from 'angular2-notifications/src/options.type'; +import {notificationIcons} from '@modules/shared/services/notification.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', - styleUrls: ['./app.component.less'] + styleUrls: ['./app.component.less', '../modules/shared/notifications.less'] }) export class AppComponent { @@ -34,7 +35,9 @@ export class AppComponent { timeOut: 5000, showProgressBar: true, pauseOnHover: true, - preventLastDuplicates: 'visible' + preventLastDuplicates: 'visible', + theClass: 'app-notification', + icons: notificationIcons }; constructor( diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.spec.ts index 655d820..1cede8c 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.spec.ts @@ -49,6 +49,8 @@ import {LogsStateService} from '@app/services/storage/logs-state.service'; import {RoutingUtilsService} from '@app/services/routing-utils.service'; import {LogsFilteringUtilsService} from '@app/services/logs-filtering-utils.service'; import {RouterTestingModule} from '@angular/router/testing'; +import {NotificationsService} from 'angular2-notifications/src/notifications.service'; +import {NotificationService} from '@modules/shared/services/notification.service'; describe('LogIndexFilterComponent', () => { let component: LogIndexFilterComponent; @@ -103,7 +105,9 @@ describe('LogIndexFilterComponent', () => { ClusterSelectionService, RoutingUtilsService, LogsFilteringUtilsService, - LogsStateService + LogsStateService, + NotificationsService, + NotificationService ], schemas: [CUSTOM_ELEMENTS_SCHEMA] }) diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.ts index a65555f..f9153b9 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-index-filter/log-index-filter.component.ts @@ -16,7 +16,7 @@ * limitations under the License. */ -import {Component, OnInit, Output, EventEmitter, forwardRef} from '@angular/core'; +import {Component, OnInit, Output, EventEmitter, forwardRef, OnDestroy} from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/operator/map'; @@ -30,6 +30,7 @@ import {UserSettingsService} from '@app/services/user-settings.service'; import {UtilsService} from '@app/services/utils.service'; import {ClustersService} from '@app/services/storage/clusters.service'; import {HostsService} from '@app/services/storage/hosts.service'; +import {Subscription} from 'rxjs/Subscription'; @Component({ selector: 'log-index-filter', @@ -43,18 +44,7 @@ import {HostsService} from '@app/services/storage/hosts.service'; } ] }) -export class LogIndexFilterComponent implements OnInit, ControlValueAccessor { - - constructor( - private logsContainer: LogsContainerService, private settingsService: UserSettingsService, - private utils: UtilsService, private clustersStorage: ClustersService, private hostsStorage: HostsService - ) { - } - - ngOnInit() { - this.changeIsSubmitDisabled.emit(true); - this.clusters.subscribe((clusters: string[]) => this.settingsService.loadIndexFilterConfig(clusters)); - } +export class LogIndexFilterComponent implements OnInit, OnDestroy, ControlValueAccessor { @Output() changeIsSubmitDisabled: EventEmitter<boolean> = new EventEmitter(); @@ -75,11 +65,30 @@ export class LogIndexFilterComponent implements OnInit, ControlValueAccessor { activeClusterName: string = ''; + private subscriptions: Subscription[] = []; + /** * Configs for all clusters */ private configs: HomogeneousObject<LogIndexFilterComponentConfig[]>; + constructor( + private logsContainer: LogsContainerService, private settingsService: UserSettingsService, + private utils: UtilsService, private clustersStorage: ClustersService, private hostsStorage: HostsService + ) { + } + + ngOnInit() { + this.changeIsSubmitDisabled.emit(true); + this.subscriptions.push( + this.clusters.subscribe((clusters: string[]) => this.settingsService.loadIndexFilterConfig(clusters)) + ); + } + + ngOnDestroy() { + this.subscriptions.forEach((subscription: Subscription) => subscription.unsubscribe()); + } + /** * Configs for selected cluster * @returns {LogIndexFilterComponentConfig[]} @@ -169,7 +178,7 @@ export class LogIndexFilterComponent implements OnInit, ControlValueAccessor { } updateValue(): void { - if(this.onChange) { + if (this.onChange) { this.onChange(this.configs); } } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts index 87b6b64..b6bcaa2 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts @@ -49,6 +49,8 @@ import {RouterTestingModule} from '@angular/router/testing'; import {LogsStateService} from '@app/services/storage/logs-state.service'; import {RoutingUtilsService} from '@app/services/routing-utils.service'; import {LogsFilteringUtilsService} from '@app/services/logs-filtering-utils.service'; +import {NotificationsService} from 'angular2-notifications/src/notifications.service'; +import {NotificationService} from '@modules/shared/services/notification.service'; describe('TimeZonePickerComponent', () => { let component: TimeZonePickerComponent; @@ -102,7 +104,9 @@ describe('TimeZonePickerComponent', () => { ClusterSelectionService, RoutingUtilsService, LogsFilteringUtilsService, - LogsStateService + LogsStateService, + NotificationsService, + NotificationService ], }) .compileComponents(); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/notifications.less b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/notifications.less new file mode 100644 index 0000000..7a0a045 --- /dev/null +++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/notifications.less @@ -0,0 +1,66 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@import "variables"; + +/deep/ simple-notification .simple-notification.app-notification { + color: @notification-color; + border-radius: 2px; + box-shadow: 0 5px 15px rgba(0,0,0,.5); + &.success { + border-left: 3px solid @form-success-color; + .fa { + color: @form-success-color; + } + } + &.info { + border-left: 3px solid @form-info-color; + .fa { + color: @form-info-color; + } + } + &.error { + border-left: 3px solid @form-error-color; + .fa { + color: @form-error-color; + } + } + .fa { + font-size: 16px; + position: absolute; + right: .25em; + top: .25em; + } + &.error, &.success, &.alert, &.info { + background: @notification-background-color; + } + .sn-title { + font-size: @notification-title-font-size; + padding: 0; + } + .sn-content { + font-size: @notification-content-font-size; + padding: 0; + } + .sn-progress-loader { + height: 2px; + span { + background: @unknown-color; + } + } +} diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/services/notification.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/services/notification.service.ts index 1406b24..e6b39ee 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/services/notification.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/services/notification.service.ts @@ -22,15 +22,24 @@ import {NotificationsService as Angular2NotificationsService} from 'angular2-not import {Notification} from 'angular2-notifications/src/notification.type'; import {NotificationInterface} from '../interfaces/notification.interface'; +import {Icons, defaultIcons} from 'angular2-notifications/src/icons'; import {TranslateService} from '@ngx-translate/core'; export enum NotificationType { SUCCESS = 'success', INFO = 'info', ERROR = 'error', - WARNING = 'warning' + ALERT = 'alert' } +export const notificationIcons: Icons = { + success: `<i class="fa fa-check"></i>`, + info: `<i class="fa fa-info-circle"></i>`, + error: `<i class="fa fa-exclamation-circle"></i>`, + alert: `<i class="fa fa-bell"></i>` +}; +Object.assign(defaultIcons, notificationIcons); + @Injectable() export class NotificationService { @@ -42,7 +51,11 @@ export class NotificationService { addNotification(payload: NotificationInterface): Notification { const {message, title, ...config} = payload; const method: string = typeof this.notificationService[config.type] === 'function' ? config.type : 'info'; - return this.notificationService[method](this.translateService.instant(title), this.translateService.instant(message), config); + return this.notificationService[method]( + this.translateService.instant(title), + this.translateService.instant(message), + {...config, icon: notificationIcons[method] || notificationIcons['info']} + ); } } diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less index 7ee8faf..55db343 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less +++ b/ambari-logsearch/ambari-logsearch-web/src/app/modules/shared/variables.less @@ -46,6 +46,7 @@ @form-success-color: #1eb475; @form-error-color: #ef6162; @form-warning-color: #e98a40; +@form-info-color: @debug-color; @fatal-color: #830A0A; @@ -84,3 +85,9 @@ // Modals @large-modal-width: 1200px; + +// Notifications +@notification-color: @base-font-color; +@notification-title-font-size: 16px; +@notification-content-font-size: 12px; +@notification-background-color: #FFF; diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.spec.ts index 4de0aa6..7dcca94 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.spec.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.spec.ts @@ -43,6 +43,8 @@ import {RouterTestingModule} from '@angular/router/testing'; import {RoutingUtilsService} from '@app/services/routing-utils.service'; import {LogsFilteringUtilsService} from '@app/services/logs-filtering-utils.service'; import {LogsStateService} from '@app/services/storage/logs-state.service'; +import {NotificationsService} from 'angular2-notifications/src/notifications.service'; +import {NotificationService} from '@modules/shared/services/notification.service'; describe('UserSettingsService', () => { beforeEach(() => { @@ -87,7 +89,9 @@ describe('UserSettingsService', () => { ClusterSelectionService, RoutingUtilsService, LogsFilteringUtilsService, - LogsStateService + LogsStateService, + NotificationsService, + NotificationService ] }); }); diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.ts index 5992809..b972d6f 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/user-settings.service.ts @@ -27,18 +27,12 @@ import {LogsContainerService} from '@app/services/logs-container.service'; import {HttpClientService} from '@app/services/http-client.service'; import {UtilsService} from '@app/services/utils.service'; import {AppSettingsService} from '@app/services/storage/app-settings.service'; +import {TranslateService} from '@ngx-translate/core'; +import {NotificationService} from '@modules/shared/services/notification.service'; @Injectable() export class UserSettingsService { - constructor(private logsContainer: LogsContainerService, private httpClient: HttpClientService, - private utils: UtilsService, private settingsStorage: AppSettingsService) { - settingsStorage.getParameter('logIndexFilters').subscribe((filters: HomogeneousObject<HomogeneousObject<Filter>>): void => { - const configs = this.parseLogIndexFilterObjects(filters); - this.settingsFormGroup.controls.logIndexFilter.setValue(configs); - }); - } - settingsFormGroup: FormGroup = new FormGroup({ logIndexFilter: new FormControl() }); @@ -49,10 +43,23 @@ export class UserSettingsService { readonly levelNames = this.logsContainer.logLevels.map((level: LogLevelObject): LogLevel => level.name); + constructor( + private logsContainer: LogsContainerService, + private httpClient: HttpClientService, + private utils: UtilsService, + private settingsStorage: AppSettingsService, + private translateService: TranslateService, + private notificationService: NotificationService) { + settingsStorage.getParameter('logIndexFilters').subscribe((filters: HomogeneousObject<HomogeneousObject<Filter>>): void => { + const configs = this.parseLogIndexFilterObjects(filters); + this.settingsFormGroup.controls.logIndexFilter.setValue(configs); + }); + } + loadIndexFilterConfig(clusterNames: string[]): void { - let processedRequests: number = 0, - allFilters: HomogeneousObject<Filter> = {}, - totalCount = clusterNames.length; + let processedRequests: number = 0; + const allFilters: HomogeneousObject<Filter> = {}; + const totalCount = clusterNames.length; clusterNames.forEach((clusterName: string): void => { this.httpClient.get('logIndexFilters', null, { clusterName @@ -71,11 +78,32 @@ export class UserSettingsService { }); } + handleLogIndexFilterUpdate = (response: Response, cluster?: string): void => { + const title: string = this.translateService.instant('logIndexFilter.update.title'); + const resultStr: string = response instanceof Response && response.ok ? 'success' : 'failed'; + const data: {[key: string]: any} = response instanceof Response ? response.json() : {}; + const message: string = this.translateService.instant(`logIndexFilter.update.${resultStr}`, { + message: '', + cluster: cluster || '', + ...data + }); + this.notificationService.addNotification({ + type: resultStr, + title, + message + }); + } + saveIndexFilterConfig(): void { - const savedValue = this.currentValues.logIndexFilter, - newValue = this.settingsFormGroup.controls.logIndexFilter.value, - clusters = Object.keys(newValue); - let storedValue = {}; + const savedValue = this.currentValues.logIndexFilter; + const newValue = this.settingsFormGroup.controls.logIndexFilter.value; + const clusters = Object.keys(newValue); + const storedValue = {}; + const addResponseHandler = (cluster: string) => { + return (response: Response) => { + this.handleLogIndexFilterUpdate(response, cluster); + }; + }; clusters.forEach((clusterName: string): void => { const savedConfig = savedValue[clusterName], newConfig = this.getLogIndexFilterObject(newValue[clusterName]); @@ -87,7 +115,7 @@ export class UserSettingsService { filter: newConfig }, null, { clusterName - }); + }).subscribe(addResponseHandler(clusterName), addResponseHandler(clusterName)); } }); this.settingsStorage.setParameter('logIndexFilters', storedValue); 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 98b16df..8628033 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json +++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json @@ -212,6 +212,9 @@ "logIndexFilter.addHosts": "Add Hosts", "logIndexFilter.hostname": "Hostname", "logIndexFilter.expiryDate": "Expiry Date", + "logIndexFilter.update.title": "Log Index Filter Update", + "logIndexFilter.update.success": "Log Index Filter for cluster {{cluster}} has been successfully updated.", + "logIndexFilter.update.error": "Error at updating Log Index Filter for cluster {{cluster}}. {{message}}", "login.title": "Login", diff --git a/ambari-logsearch/ambari-logsearch-web/src/mockdata/mock-data-get.ts b/ambari-logsearch/ambari-logsearch-web/src/mockdata/mock-data-get.ts index 72e6b7d..ab5b235 100644 --- a/ambari-logsearch/ambari-logsearch-web/src/mockdata/mock-data-get.ts +++ b/ambari-logsearch/ambari-logsearch-web/src/mockdata/mock-data-get.ts @@ -1769,7 +1769,7 @@ export const mockDataGet = { configurationUploaded: true } }, - 'api/v1/shipper/filters/\[a-zA-Z0-9\]/level': { + 'api/v1/shipper/filters/\[a-zA-Z0-9\]{1,}/level': { 'filter': { 'ambari_agent': { 'label': 'ambari_agent', -- To stop receiving notification emails like this one, please contact ababiic...@apache.org.