You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rl...@apache.org on 2017/11/29 16:58:23 UTC
[06/24] ambari git commit: AMBARI-22531 Log Search UI: refine search
box. (ababiichuk)
AMBARI-22531 Log Search UI: refine search box. (ababiichuk)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/2bf3c8ed
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/2bf3c8ed
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/2bf3c8ed
Branch: refs/heads/branch-feature-AMBARI-20859
Commit: 2bf3c8edb26a6f0aac43699483e5ebee89dcc533
Parents: 33ee1a7
Author: ababiichuk <ab...@hortonworks.com>
Authored: Tue Nov 28 16:13:51 2017 +0200
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Tue Nov 28 16:49:30 2017 +0200
----------------------------------------------------------------------
.../src/app/classes/filtering.ts | 22 +++-
.../src/app/classes/models/app-state.ts | 3 +-
.../classes/queries/audit-logs-query-params.ts | 3 +-
.../service-logs-truncated-query-params.ts | 3 +-
.../src/app/classes/string.ts | 25 ++++
.../filters-panel/filters-panel.component.html | 8 +-
.../filters-panel.component.spec.ts | 1 +
.../filters-panel/filters-panel.component.ts | 99 ++++++++-------
.../logs-container.component.html | 2 +-
.../logs-container/logs-container.component.ts | 5 +-
.../src/app/components/mixins.less | 2 +-
.../search-box/search-box.component.html | 24 ++--
.../search-box/search-box.component.less | 23 ++--
.../search-box/search-box.component.ts | 122 ++++++++++++++-----
.../src/app/components/variables.less | 6 +-
.../services/component-actions.service.spec.ts | 4 +-
.../app/services/component-actions.service.ts | 2 +-
.../component-generator.service.spec.ts | 4 +-
.../app/services/logs-container.service.spec.ts | 4 +-
.../src/app/services/logs-container.service.ts | 69 ++++++++---
.../src/app/services/utils.service.ts | 4 +
21 files changed, 303 insertions(+), 132 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
index 2a7205f..d92dd41 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
@@ -18,9 +18,10 @@
import {Moment, unitOfTime} from 'moment';
import {ListItem} from '@app/classes/list-item';
+import {TimeRangeType, SortingType} from '@app/classes/string';
export interface TimeUnit {
- type: 'CURRENT' | 'LAST' | 'PAST';
+ type: TimeRangeType;
unit: unitOfTime.DurationConstructor;
interval?: number;
}
@@ -33,7 +34,7 @@ export interface CustomTimeRange {
export interface SortingConditions {
key: string;
- type: 'asc' | 'desc';
+ type: SortingType;
}
export interface TimeUnitListItem extends ListItem {
@@ -49,4 +50,21 @@ export interface FilterCondition {
options?: (ListItem | TimeUnitListItem[])[];
defaultSelection?: ListItem | ListItem[] | number;
iconClass?: string;
+ fieldName?: string;
+}
+
+export interface SearchBoxParameter {
+ name: string;
+ value: string;
+ isExclude: boolean;
+}
+
+export interface SearchBoxParameterProcessed extends SearchBoxParameter {
+ id: number;
+ label: string;
+}
+
+export interface SearchBoxParameterTriggered {
+ value: string;
+ isExclude: boolean;
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
index afed497..c3279ce 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
@@ -17,12 +17,13 @@
*/
import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry';
+import {LogsType} from '@app/classes/string';
export interface AppState {
isAuthorized: boolean;
isInitialLoading: boolean;
isLoginInProgress: boolean;
- activeLogsType?: string;
+ activeLogsType?: LogsType;
isServiceLogsFileView: boolean;
isServiceLogContextView: boolean;
activeLog: ActiveServiceLogEntry | null;
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
index 509fa04..3b38a03 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.ts
@@ -17,6 +17,7 @@
*/
import {QueryParams} from '@app/classes/queries/query-params';
+import {SortingType} from '@app/classes/string';
export const defaultParams = {
page: '0',
@@ -35,7 +36,7 @@ export class AuditLogsQueryParams extends QueryParams {
pageSize: string;
startIndex: string;
sortBy?: string;
- sortType?: 'asc' | 'desc';
+ sortType?: SortingType;
clusters?: string;
mustBe?: string;
mustNot?: string;
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts
index 6f9de16..3b08e11 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-truncated-query-params.ts
@@ -17,6 +17,7 @@
*/
import {QueryParams} from '@app/classes/queries/query-params';
+import {ScrollType} from '@app/classes/string';
export const defaultParams = {
numberRows: '10',
@@ -32,5 +33,5 @@ export class ServiceLogsTruncatedQueryParams extends QueryParams {
host_name: string;
component_name: string;
numberRows: string;
- scrollType: 'before' | 'after' | '';
+ scrollType: ScrollType;
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts
new file mode 100644
index 0000000..21ff4ca
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/string.ts
@@ -0,0 +1,25 @@
+/**
+ * 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.
+ */
+
+export type LogsType = 'auditLogs' | 'serviceLogs';
+
+export type TimeRangeType = 'CURRENT' | 'LAST' | 'PAST';
+
+export type SortingType = 'asc' | 'desc';
+
+export type ScrollType = 'before' | 'after' | '';
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
index 2d327a6..4fe169d 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
@@ -20,9 +20,9 @@
<filter-dropdown *ngIf="isFilterConditionDisplayed('clusters')" [label]="filters.clusters.label"
formControlName="clusters" [options]="filters.clusters.options" [isMultipleChoice]="true"
class="filter-input"></filter-dropdown>
- <search-box formControlName="query" [items]="searchBoxItemsTranslated" class="filter-input"
- [parameterNameChangeSubject]="queryParameterNameChange"
- [parameterAddSubject]="queryParameterAdd"></search-box>
+ <search-box [parameterAddSubject]="queryParameterAdd" [parameterNameChangeSubject]="queryParameterNameChange"
+ formControlName="query" [items]="searchBoxItemsTranslated" [itemsOptions]="options"
+ class="filter-input"></search-box>
<time-range-picker *ngIf="isFilterConditionDisplayed('timeRange')" formControlName="timeRange"
class="filter-input"></time-range-picker>
<timezone-picker class="filter-input"></timezone-picker>
@@ -31,7 +31,7 @@
</button-->
</div>
<div class="filter-buttons col-md-4">
- <dropdown-button [options]="searchBoxItems" iconClass="fa fa-search-minus" label="filter.excluded"
+ <dropdown-button [options]="searchBoxItems | async" iconClass="fa fa-search-minus" label="filter.excluded"
[hideCaret]="true" [showSelectedValue]="false" action="proceedWithExclude"></dropdown-button>
<filter-button *ngIf="isFilterConditionDisplayed('hosts')" formControlName="hosts"
label="{{filters.hosts.label | translate}}" [iconClass]="filters.hosts.iconClass"
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
index 1f7e8db..c9f9b52 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
@@ -106,6 +106,7 @@ describe('FiltersPanelComponent', () => {
component.filtersForm = new FormGroup({
control: new FormControl()
});
+ component.logsType = 'auditLogs';
fixture.detectChanges();
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
index b41f7cd..01a8932 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
@@ -16,74 +16,83 @@
* limitations under the License.
*/
-import {Component, Input} from '@angular/core';
+import {Component, OnChanges, SimpleChanges, Input} from '@angular/core';
import {FormGroup} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
-import {TranslateService} from '@ngx-translate/core';
+import {FilterCondition} from '@app/classes/filtering';
import {ListItem} from '@app/classes/list-item';
+import {LogsType} from '@app/classes/string';
import {CommonEntry} from '@app/classes/models/common-entry';
-import {LogField} from '@app/classes/models/log-field';
import {LogsContainerService} from '@app/services/logs-container.service';
-import {AppStateService} from '@app/services/storage/app-state.service';
@Component({
selector: 'filters-panel',
templateUrl: './filters-panel.component.html',
styleUrls: ['./filters-panel.component.less']
})
-export class FiltersPanelComponent {
+export class FiltersPanelComponent implements OnChanges {
- constructor(
- private translate: TranslateService, private logsContainer: LogsContainerService,
- private appState: AppStateService
- ) {
- appState.getParameter('activeLogsType').subscribe(value => {
- this.logsType = value;
- logsContainer.logsTypeMap[value].fieldsModel.getAll().subscribe((fields: LogField[]): void => {
- if (fields.length) {
- const items = fields.filter((field: LogField): boolean => {
- return this.excludedParameters.indexOf(field.name) === -1;
- }).map((field: LogField): CommonEntry => {
- return {
- name: field.displayName || field.name,
- value: field.name
- };
- }),
- labelKeys = items.map((item: CommonEntry): string => item.name);
- this.searchBoxItems = items.map((item: CommonEntry): ListItem => {
- return {
- label: item.name,
- value: item.value
- };
- });
- translate.get(labelKeys).first().subscribe((translation: {[key: string]: string}): void => {
- this.searchBoxItemsTranslated = items.map((item: CommonEntry): CommonEntry => {
- return {
- name: translation[item.name],
- value: item.value
- };
- })
- });
- }
- })
- });
+ constructor(private logsContainer: LogsContainerService) {
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.hasOwnProperty('logsType')) {
+ let result;
+ switch (changes.logsType.currentValue) {
+ case 'auditLogs':
+ result = this.logsContainer.auditLogsColumns;
+ break;
+ case 'serviceLogs':
+ result = this.logsContainer.serviceLogsColumns;
+ break;
+ }
+ this.searchBoxItems = result;
+ }
}
@Input()
filtersForm: FormGroup;
- private readonly excludedParameters = ['cluster', 'host', 'level', 'type', 'logtime'];
-
- private logsType: string;
+ @Input()
+ logsType: LogsType;
- searchBoxItems: ListItem[] = [];
+ searchBoxItems: Observable<ListItem[]>;
- searchBoxItemsTranslated: CommonEntry[] = [];
+ get searchBoxItemsTranslated(): CommonEntry[] {
+ switch (this.logsType) {
+ case 'auditLogs':
+ return this.logsContainer.auditLogsColumnsTranslated;
+ case 'serviceLogs':
+ return this.logsContainer.serviceLogsColumnsTranslated;
+ }
+ }
- get filters(): any {
+ get filters(): {[key: string]: FilterCondition} {
return this.logsContainer.filters;
}
+ /**
+ * Object with options for search box parameter values
+ * @returns {[key: string]: CommonEntry[]}
+ */
+ get options(): {[key: string]: CommonEntry[]} {
+ return Object.keys(this.filters).filter((key: string): boolean => {
+ const condition = this.filters[key];
+ return Boolean(condition.fieldName && condition.options);
+ }).reduce((currentValue, currentKey) => {
+ const condition = this.filters[currentKey];
+ return Object.assign(currentValue, {
+ [condition.fieldName]: condition.options.map((option: ListItem): CommonEntry => {
+ return {
+ name: option.value,
+ value: option.value
+ }
+ })
+ });
+ }, {});
+ }
+
get queryParameterNameChange(): Subject<any> {
return this.logsContainer.queryParameterNameChange;
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
index f34dd15..13911bd 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
@@ -25,7 +25,7 @@
</div>
</div>
<div class="container-fluid">
- <filters-panel class="row" [filtersForm]="filtersForm"></filters-panel>
+ <filters-panel class="row" [filtersForm]="filtersForm" [logsType]="logsType"></filters-panel>
<div class="row">
<div *ngIf="autoRefreshRemainingSeconds" class="col-md-12">
<div class="auto-refresh-message pull-right">
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
index b06cfa4..86709fb 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
@@ -30,6 +30,7 @@ import {BarGraph} from '@app/classes/models/bar-graph';
import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry';
import {HistogramOptions} from '@app/classes/histogram-options';
import {ListItem} from '@app/classes/list-item';
+import {LogsType} from '@app/classes/string';
@Component({
selector: 'logs-container',
@@ -43,7 +44,7 @@ export class LogsContainerComponent {
private tabsStorage: TabsService, private logsContainer: LogsContainerService
) {
this.logsContainer.loadColumnsNames();
- appState.getParameter('activeLogsType').subscribe((value: string) => this.logsType = value);
+ appState.getParameter('activeLogsType').subscribe((value: LogsType) => this.logsType = value);
serviceLogsHistogramStorage.getAll().subscribe((data: BarGraph[]): void => {
this.histogramData = this.logsContainer.getHistogramData(data);
});
@@ -56,7 +57,7 @@ export class LogsContainerComponent {
return this.logsContainer.filtersForm;
};
- private logsType: string;
+ private logsType: LogsType;
get totalCount(): number {
return this.logsContainer.totalCount;
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
index 5fa265b..4460821 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/mixins.less
@@ -80,7 +80,7 @@
.dropdown-list-default {
line-height: 1;
- border-radius: 2px;
+ border-radius: @dropdown-border-radius;
font-size: 14px;
min-width: @dropdown-min-width;
background: #FFF;
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.html
index 92f9520..5bffdc5 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.html
@@ -16,17 +16,25 @@
-->
<label class="parameter-label" *ngFor="let parameter of parameters">
- <span *ngIf="parameter.isExclude" class="fa fa-search-minus exclude-icon"></span>
+ <span *ngIf="parameter.isExclude" class="fa fa-search-minus"></span>
{{parameter.label | translate}}:
<span class="parameter-value">{{parameter.value}}</span>
<span class="fa fa-times remove-parameter" (click)="removeParameter($event, parameter.id)"></span>
</label>
<span class="active-parameter-label" *ngIf="isActive && activeItem">{{activeItem.name | translate}}:</span>
<div [ngClass]="{'search-item-container': true, 'active': isActive, 'value': isValueInput}">
- <input #parameterInput auto-complete [(ngModel)]="currentValue" [source]="items" [list-formatter]="itemsListFormatter"
- display-property-name="name" (valueChanged)="changeParameterName({item: $event, isExclude: false})"
- class="search-item-input parameter-input form-control">
- <input #valueInput type="text" [(ngModel)]="currentValue" class="search-item-input value-input form-control"
- (keyup)="onParameterValueChange($event)">
- <div class="search-item-text" [innerHTML]="currentValue"></div>
-</div>
\ No newline at end of file
+ <span class="parameter-input-wrapper">
+ <input #parameterInput auto-complete class="search-item-input parameter-input form-control"
+ [(ngModel)]="currentValue" [source]="items" display-property-name="name"
+ [list-formatter]="itemsListFormatter" [value-formatter]="itemsValueFormatter" [match-formatted]="true"
+ (valueChanged)="changeParameterName({value: $event.value, isExclude: false})"
+ (keyup)="onParameterKeyUp($event)">
+ </span>
+ <span [ngClass]="{'no-value-options': !activeItemValueOptions.length}">
+ <input #valueInput auto-complete [(ngModel)]="currentValue" [source]="activeItemValueOptions"
+ [list-formatter]="itemsListFormatter" [value-formatter]="itemsValueFormatter" [match-formatted]="true"
+ (valueChanged)="onParameterValueChange($event.value)" (keydown)="onParameterValueKeyDown($event)"
+ (keyup)="onParameterValueKeyUp($event)" class="search-item-input value-input form-control">
+ </span>
+ <div class="search-item-text">{{currentValue}}</div>
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.less
index f0a5ce0..eac3bd6 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.less
@@ -35,16 +35,13 @@
cursor: text;
.parameter-label {
- // TODO implement actual styles
margin: @label-margin;
- padding: @label-margin;
- background-color: @main-background-color;
+ border-radius: @dropdown-border-radius;
+ padding: @search-parameter-padding;
+ background-color: @search-parameter-background-color;
+ color: @base-font-color;
font-size: 0.8em;
- .exclude-icon {
- color: @exclude-color;
- }
-
.parameter-value {
font-weight: normal;
}
@@ -94,8 +91,16 @@
}
&.value {
- /deep/ .ng2-auto-complete-wrapper, .parameter-input {
- display: none;
+ .parameter-input-wrapper {
+ /deep/ .ng2-auto-complete-wrapper {
+ display: none;
+ }
+ }
+
+ .no-value-options {
+ /deep/ .ng2-auto-complete {
+ display: none;
+ }
}
.value-input {
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
index 18ff715..14cc89b 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
@@ -19,6 +19,7 @@
import {Component, OnInit, OnDestroy, Input, ViewChild, ElementRef, forwardRef} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {Subject} from 'rxjs/Subject';
+import {SearchBoxParameter, SearchBoxParameterProcessed, SearchBoxParameterTriggered} from '@app/classes/filtering';
import {CommonEntry} from '@app/classes/models/common-entry';
import {UtilsService} from '@app/services/utils.service';
@@ -42,7 +43,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
this.rootElement.addEventListener('keydown', this.onRootKeyDown);
}
- ngOnInit() {
+ ngOnInit(): void {
this.parameterInput = this.parameterInputRef.nativeElement;
this.valueInput = this.valueInputRef.nativeElement;
this.parameterInput.addEventListener('focus', this.onParameterInputFocus);
@@ -52,7 +53,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
this.parameterAddSubject.subscribe(this.onParameterAdd);
}
- ngOnDestroy() {
+ ngOnDestroy(): void {
this.rootElement.removeEventListener('click', this.onRootClick);
this.rootElement.removeEventListener('keydown', this.onRootKeyDown);
this.parameterInput.removeEventListener('focus', this.onParameterInputFocus);
@@ -62,6 +63,8 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
this.parameterAddSubject.unsubscribe();
}
+ private readonly messageParameterName: string = 'log_message';
+
private currentId: number = 0;
private isExclude: boolean = false;
@@ -80,10 +83,13 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
items: CommonEntry[] = [];
@Input()
- parameterNameChangeSubject: Subject<any> = this.defaultSubject;
+ itemsOptions: {[key: string]: CommonEntry[]};
+
+ @Input()
+ parameterNameChangeSubject: Subject<SearchBoxParameterTriggered> = this.defaultSubject;
@Input()
- parameterAddSubject: Subject<any> = this.defaultSubject;
+ parameterAddSubject: Subject<SearchBoxParameter> = this.defaultSubject;
@ViewChild('parameterInput')
parameterInputRef: ElementRef;
@@ -93,13 +99,18 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
private rootElement: HTMLElement;
- private parameterInput: HTMLElement;
+ private parameterInput: HTMLInputElement;
+
+ private valueInput: HTMLInputElement;
- private valueInput: HTMLElement;
+ activeItem: CommonEntry | null = null;
- activeItem?: any;
+ parameters: SearchBoxParameterProcessed[] = [];
- parameters: any[] = [];
+ get activeItemValueOptions(): CommonEntry[] {
+ return this.itemsOptions && this.activeItem && this.itemsOptions[this.activeItem.value] ?
+ this.itemsOptions[this.activeItem.value] : [];
+ }
private onChange: (fn: any) => void;
@@ -133,52 +144,80 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
}
};
- private getItem(name: string): CommonEntry {
- return this.items.find(field => field.value === name);
+ private switchToParameterInput = (): void => {
+ this.activeItem = null;
+ this.isValueInput = false;
+ setTimeout(() => this.parameterInput.focus());
+ };
+
+ private getItemByValue(name: string): CommonEntry {
+ return this.items.find((field: CommonEntry): boolean => field.value === name);
+ }
+
+ private getItemByName(name: string): CommonEntry {
+ return this.items.find((field: CommonEntry): boolean => field.name === name);
}
clear(): void {
this.isActive = false;
this.activeItem = null;
- this.currentValue = null;
+ this.currentValue = '';
+ this.parameterInput.value = '';
+ this.valueInput.value = '';
}
itemsListFormatter(item: CommonEntry): string {
return item.name;
}
- changeParameterName(item: any): void {
- this.parameterNameChangeSubject.next(item);
+ itemsValueFormatter(item: CommonEntry): string {
+ return item.value;
}
- onParameterNameChange = (options: any): void => {
- this.activeItem = typeof options.item === 'string' ? this.getItem(options.item) : options.item;
- this.isExclude = options.isExclude;
- this.isActive = true;
- this.isParameterInput = false;
- this.isValueInput = true;
- this.currentValue = '';
- setTimeout(() => this.valueInput.focus(), 0);
+ changeParameterName(options: SearchBoxParameterTriggered): void {
+ this.parameterNameChangeSubject.next(options);
+ }
+
+ onParameterNameChange = (options: SearchBoxParameterTriggered): void => {
+ if (options.value) {
+ this.activeItem = this.getItemByValue(options.value);
+ this.isExclude = options.isExclude;
+ this.isActive = true;
+ this.isParameterInput = false;
+ this.isValueInput = true;
+ this.currentValue = '';
+ setTimeout(() => this.valueInput.focus(), 0);
+ }
};
- onParameterValueChange(event: KeyboardEvent): void {
+ onParameterValueKeyDown(event: KeyboardEvent): void {
+ if (this.utils.isBackSpacePressed(event) && !this.currentValue) {
+ this.switchToParameterInput();
+ }
+ }
+
+ onParameterValueKeyUp(event: KeyboardEvent): void {
if (this.utils.isEnterPressed(event) && this.currentValue) {
+ this.onParameterValueChange(this.currentValue);
+ }
+ }
+
+ onParameterValueChange(value: string): void {
+ if (value) {
this.parameters.push({
id: this.currentId++,
name: this.activeItem.value,
label: this.activeItem.name,
- value: this.currentValue,
+ value: value,
isExclude: this.isExclude
});
- this.currentValue = '';
- this.activeItem = null;
- this.isValueInput = false;
this.updateValue();
}
+ this.switchToParameterInput();
}
- onParameterAdd = (options: any): void => {
- const item = this.getItem(options.name);
+ onParameterAdd = (options: SearchBoxParameter): void => {
+ const item = this.getItemByValue(options.name);
this.parameters.push({
id: this.currentId++,
name: options.name,
@@ -189,19 +228,38 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
this.updateValue();
};
+ onParameterKeyUp = (event: KeyboardEvent): void => {
+ if (this.utils.isEnterPressed(event) && this.currentValue) {
+ const existingItem = this.getItemByName(this.currentValue);
+ if (existingItem) {
+ this.changeParameterName({
+ value: this.currentValue,
+ isExclude: false
+ });
+ } else {
+ this.parameterAddSubject.next({
+ name: this.messageParameterName,
+ value: this.currentValue,
+ isExclude: false
+ });
+ }
+ }
+ };
+
removeParameter(event: MouseEvent, id: number): void {
- this.parameters = this.parameters.filter(parameter => parameter.id !== id);
+ this.parameters = this.parameters.filter((parameter: SearchBoxParameterProcessed): boolean => parameter.id !== id);
this.updateValue();
event.stopPropagation();
}
- updateValue() {
+ updateValue(): void {
+ this.currentValue = '';
if (this.onChange) {
this.onChange(this.parameters);
}
}
- writeValue(parameters: any [] = []) {
+ writeValue(parameters: SearchBoxParameterProcessed[] = []): void {
this.parameters = parameters;
this.updateValue();
}
@@ -210,7 +268,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
this.onChange = callback;
}
- registerOnTouched() {
+ registerOnTouched(): void {
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
index 7b7fcae..18268ad 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
@@ -23,7 +23,6 @@
@button-border-radius: 4px;
@input-border-width: 1px;
@input-border: @input-border-width solid #CFD3D7;
-@button-border-radius: 4px;
@input-group-addon-padding: 6px 12px 6px 0;
@block-margin-top: 20px;
@link-color: #1491C1;
@@ -37,9 +36,11 @@
@checkbox-top: 4px;
@dropdown-min-width: 160px;
@dropdown-max-height: 500px; // TODO get rid of magic number, base on actual design
+@dropdown-border-radius: 2px;
@input-height: 34px;
@input-padding: 10px;
@col-padding: 15px;
+@search-parameter-padding: 5px 2px;
@fatal-color: #830A0A;
@error-color: #E81D1D;
@@ -51,6 +52,7 @@
@submit-color: #5CB85C;
@submit-hover-color: #449D44;
@exclude-color: #EF6162;
+@search-parameter-background-color: #DDD;
// Panels
@panel-heading: rgba(255, 255, 255, 1);
@@ -63,4 +65,4 @@
@icon-padding: 5px;
// Table
-@table-border-color: #EEEEEE;
+@table-border-color: #EEE;
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
index 6d43ff1..c2cee8d 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
@@ -17,6 +17,7 @@
*/
import {TestBed, inject} from '@angular/core/testing';
+import {TranslationModules} from '@app/test-config.spec';
import {StoreModule} from '@ngrx/store';
import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
import {AppStateService, appState} from '@app/services/storage/app-state.service';
@@ -62,7 +63,8 @@ describe('ComponentActionsService', () => {
serviceLogsHistogramData,
serviceLogsTruncated,
tabs
- })
+ }),
+ ...TranslationModules
],
providers: [
ComponentActionsService,
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
index e796183..0fc9fde 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
@@ -132,7 +132,7 @@ export class ComponentActionsService {
}
proceedWithExclude = (item: string): void => this.logsContainer.queryParameterNameChange.next({
- item: item,
+ value: item,
isExclude: true
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
index a161190..3f65cd1 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
@@ -17,6 +17,7 @@
*/
import {TestBed, inject} from '@angular/core/testing';
+import {TranslationModules} from '@app/test-config.spec';
import {StoreModule} from '@ngrx/store';
import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
@@ -60,7 +61,8 @@ describe('ComponentGeneratorService', () => {
components,
serviceLogsTruncated,
tabs
- })
+ }),
+ ...TranslationModules
],
providers: [
ComponentGeneratorService,
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
index 47cb25d..870058b 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
@@ -17,6 +17,7 @@
*/
import {TestBed, inject} from '@angular/core/testing';
+import {TranslationModules} from '@app/test-config.spec';
import {StoreModule} from '@ngrx/store';
import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
@@ -61,7 +62,8 @@ describe('LogsContainerService', () => {
hosts,
serviceLogsTruncated,
tabs
- })
+ }),
+ ...TranslationModules
],
providers: [
AuditLogsService,
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
index a715adc..64b14b8 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
@@ -27,6 +27,7 @@ import 'rxjs/add/operator/first';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/takeUntil';
import * as moment from 'moment-timezone';
+import {TranslateService} from '@ngx-translate/core';
import {HttpClientService} from '@app/services/http-client.service';
import {AuditLogsService} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService} from '@app/services/storage/audit-logs-fields.service';
@@ -41,8 +42,11 @@ import {ClustersService} from '@app/services/storage/clusters.service';
import {ComponentsService} from '@app/services/storage/components.service';
import {HostsService} from '@app/services/storage/hosts.service';
import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry';
-import {FilterCondition, TimeUnitListItem, SortingListItem} from '@app/classes/filtering';
+import {
+ FilterCondition, TimeUnitListItem, SortingListItem, SearchBoxParameter, SearchBoxParameterTriggered
+} from '@app/classes/filtering';
import {ListItem} from '@app/classes/list-item';
+import {LogsType, ScrollType, SortingType} from '@app/classes/string';
import {Tab} from '@app/classes/models/tab';
import {LogField} from '@app/classes/models/log-field';
import {AuditLog} from '@app/classes/models/audit-log';
@@ -51,14 +55,15 @@ import {ServiceLog} from '@app/classes/models/service-log';
import {ServiceLogField} from '@app/classes/models/service-log-field';
import {BarGraph} from '@app/classes/models/bar-graph';
import {NodeItem} from '@app/classes/models/node-item';
+import {CommonEntry} from '@app/classes/models/common-entry';
@Injectable()
export class LogsContainerService {
constructor(
- private httpClient: HttpClientService, private auditLogsStorage: AuditLogsService,
- private auditLogsFieldsStorage: AuditLogsFieldsService, private serviceLogsStorage: ServiceLogsService,
- private serviceLogsFieldsStorage: ServiceLogsFieldsService,
+ private translate: TranslateService, private httpClient: HttpClientService,
+ private auditLogsStorage: AuditLogsService, private auditLogsFieldsStorage: AuditLogsFieldsService,
+ private serviceLogsStorage: ServiceLogsService, private serviceLogsFieldsStorage: ServiceLogsFieldsService,
private serviceLogsHistogramStorage: ServiceLogsHistogramDataService,
private serviceLogsTruncatedStorage: ServiceLogsTruncatedService, private appState: AppStateService,
private appSettings: AppSettingsService, private tabsStorage: TabsService, private clustersStorage: ClustersService,
@@ -78,7 +83,7 @@ export class LogsContainerService {
this.loadHosts();
appState.getParameter('activeLog').subscribe((value: ActiveServiceLogEntry | null) => this.activeLog = value);
appState.getParameter('isServiceLogsFileView').subscribe((value: boolean) => this.isServiceLogsFileView = value);
- appState.getParameter('activeLogsType').subscribe((value: string) => this.activeLogsType = value);
+ appState.getParameter('activeLogsType').subscribe((value: LogsType) => this.activeLogsType = value);
appSettings.getParameter('timeZone').subscribe((value: string) => this.timeZone = value || this.defaultTimeZone);
tabsStorage.mapCollection((tab: Tab): Tab => {
let currentAppState = tab.appState || {};
@@ -111,6 +116,8 @@ export class LogsContainerService {
this.loadLogs();
});
});
+ this.auditLogsColumns.subscribe(this.getTranslationKeysSubscriber('auditLogsColumnsTranslated'));
+ this.serviceLogsColumns.subscribe(this.getTranslationKeysSubscriber('serviceLogsColumnsTranslated'));
}
private readonly paginationOptions: string[] = ['10', '25', '50', '100'];
@@ -119,7 +126,8 @@ export class LogsContainerService {
clusters: {
label: 'filter.clusters',
options: [],
- defaultSelection: []
+ defaultSelection: [],
+ fieldName: 'cluster'
},
timeRange: {
options: [
@@ -346,7 +354,8 @@ export class LogsContainerService {
label: 'filter.components',
iconClass: 'fa fa-cubes',
options: [],
- defaultSelection: []
+ defaultSelection: [],
+ fieldName: 'type'
},
levels: {
label: 'filter.levels',
@@ -381,13 +390,15 @@ export class LogsContainerService {
value: 'UNKNOWN'
}
],
- defaultSelection: []
+ defaultSelection: [],
+ fieldName: 'level'
},
hosts: {
label: 'filter.hosts',
iconClass: 'fa fa-server',
options: [],
- defaultSelection: []
+ defaultSelection: [],
+ fieldName: 'host'
},
auditLogsSorting: {
label: 'sorting.title',
@@ -533,7 +544,7 @@ export class LogsContainerService {
activeLog: ActiveServiceLogEntry | null = null;
- activeLogsType: string;
+ activeLogsType: LogsType;
private filtersFormChange: Subject<any> = new Subject();
@@ -561,10 +572,30 @@ export class LogsContainerService {
}
}
+ private getTranslationKeysSubscriber = (propertyName: string): (items: ListItem[]) => void => {
+ return (items: ListItem[]): void => {
+ const keys = items.map((item: ListItem): string => item.label);
+ if (keys.length) {
+ this.translate.get(keys).first().subscribe((translation: {[key: string]: string}): void => {
+ this[propertyName] = items.map((item: ListItem): CommonEntry => {
+ return {
+ name: translation[item.label],
+ value: item.value
+ };
+ });
+ });
+ }
+ };
+ };
+
auditLogsColumns: Observable<ListItem[]> = this.auditLogsFieldsStorage.getAll().map(this.columnsMapper);
+ auditLogsColumnsTranslated: CommonEntry[] = [];
+
serviceLogsColumns: Observable<ListItem[]> = this.serviceLogsFieldsStorage.getAll().map(this.columnsMapper);
+ serviceLogsColumnsTranslated: CommonEntry[] = [];
+
serviceLogs: Observable<ServiceLog[]> = Observable.combineLatest(this.serviceLogsStorage.getAll(), this.serviceLogsColumns).map(this.logsMapper);
auditLogs: Observable<AuditLog[]> = Observable.combineLatest(this.auditLogsStorage.getAll(), this.auditLogsColumns).map(this.logsMapper);
@@ -593,9 +624,9 @@ export class LogsContainerService {
};
}
- queryParameterNameChange: Subject<any> = new Subject();
+ queryParameterNameChange: Subject<SearchBoxParameterTriggered> = new Subject();
- queryParameterAdd: Subject<any> = new Subject();
+ queryParameterAdd: Subject<SearchBoxParameter> = new Subject();
private stopTimer: Subject<any> = new Subject();
@@ -611,7 +642,7 @@ export class LogsContainerService {
private stopCaptureTime: number;
- loadLogs = (logsType: string = this.activeLogsType): void => {
+ loadLogs = (logsType: LogsType = this.activeLogsType): void => {
this.httpClient.get(logsType, this.getParams('listFilters')).subscribe((response: Response): void => {
const jsonResponse = response.json(),
model = this.logsTypeMap[logsType].logsModel;
@@ -640,7 +671,7 @@ export class LogsContainerService {
}
};
- loadLogContext(id: string, hostName: string, componentName: string, scrollType: 'before' | 'after' | '' = ''): void {
+ loadLogContext(id: string, hostName: string, componentName: string, scrollType: ScrollType = ''): void {
const params = {
id: id,
host_name: hostName,
@@ -671,7 +702,7 @@ export class LogsContainerService {
});
}
- private getParams(filtersMapName: string, logsType: string = this.activeLogsType): {[key: string]: string} {
+ private getParams(filtersMapName: string, logsType: LogsType = this.activeLogsType): {[key: string]: string} {
let params = {};
this.logsTypeMap[logsType][filtersMapName].forEach((key: string): void => {
const inputValue = this.filtersForm.getRawValue()[key],
@@ -787,11 +818,11 @@ export class LogsContainerService {
return endMoment ? endMoment.toISOString() : '';
};
- private getQuery(isExclude: boolean): (value: any[]) => string {
- return (value: any[]): string => {
+ private getQuery(isExclude: boolean): (value: SearchBoxParameter[]) => string {
+ return (value: SearchBoxParameter[]): string => {
let parameters;
if (value && value.length) {
- parameters = value.filter(item => item.isExclude === isExclude).map(parameter => {
+ parameters = value.filter((item: SearchBoxParameter): boolean => item.isExclude === isExclude).map((parameter: SearchBoxParameter): {[key: string]: string} => {
return {
[parameter.name]: parameter.value.replace(/\s/g, '+')
};
@@ -801,7 +832,7 @@ export class LogsContainerService {
}
}
- private getSortType(selection: SortingListItem[] = []): 'asc' | 'desc' {
+ private getSortType(selection: SortingListItem[] = []): SortingType {
return selection[0] && selection[0].value ? selection[0].value.type : 'desc';
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/2bf3c8ed/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
index 175b585..dd9075c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
@@ -77,6 +77,10 @@ export class UtilsService {
return event.keyCode === 13;
}
+ isBackSpacePressed(event: KeyboardEvent): boolean {
+ return event.keyCode === 8;
+ }
+
isDifferentDates(dateA, dateB, timeZone): boolean {
const momentA = moment(dateA).tz(timeZone),
momentB = moment(dateB).tz(timeZone);