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 2018/01/05 23:04:04 UTC
[08/50] [abbrv] ambari git commit: AMBARI-22714 Log Search UI:
implement Summary tab for Access Logs page. (ababiichuk)
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.spec.ts
new file mode 100644
index 0000000..14fa60e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.spec.ts
@@ -0,0 +1,50 @@
+/**
+ * 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 {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {TranslationModules} from '@app/test-config.spec';
+import {GraphLegendItemComponent} from '@app/components/graph-legend-item/graph-legend-item.component';
+
+import {GraphTooltipComponent} from './graph-tooltip.component';
+
+describe('GraphTooltipComponent', () => {
+ let component: GraphTooltipComponent;
+ let fixture: ComponentFixture<GraphTooltipComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ GraphTooltipComponent,
+ GraphLegendItemComponent
+ ],
+ imports: TranslationModules,
+ schemas: [CUSTOM_ELEMENTS_SCHEMA]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(GraphTooltipComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.ts
new file mode 100644
index 0000000..9d26a2e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.ts
@@ -0,0 +1,36 @@
+/**
+ * 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 {Component, Input} from '@angular/core';
+
+@Component({
+ selector: 'graph-tooltip',
+ templateUrl: './graph-tooltip.component.html',
+ styleUrls: ['./graph-tooltip.component.less']
+})
+export class GraphTooltipComponent {
+
+ @Input()
+ title: string | number = '';
+
+ @Input()
+ data = [];
+
+ @Input()
+ labelClass: string = 'initial-color';
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.html
new file mode 100644
index 0000000..015013f
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.html
@@ -0,0 +1,22 @@
+<!--
+ 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.
+-->
+
+<div #graphContainer></div>
+<graph-legend class="col-md-12" [items]="legendItems"></graph-legend>
+<graph-tooltip #tooltip [data]="tooltipInfo.data" [title]="tooltipInfo.title" [ngClass]="{'hide': !tooltipInfo.data}"
+ [style.top]="tooltipInfo.data ? tooltipPosition.top + 'px' : ''"
+ [style.left]="tooltipInfo.data ? tooltipPosition.left + 'px' : ''"></graph-tooltip>
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.less
new file mode 100644
index 0000000..395737a
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.less
@@ -0,0 +1,22 @@
+/**
+ * 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';
+
+:host {
+ padding-top: @graph-padding;
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.spec.ts
new file mode 100644
index 0000000..2c59916
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.spec.ts
@@ -0,0 +1,61 @@
+/**
+ * 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 {Injector} from '@angular/core';
+import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
+import {TranslationModules} from '@app/test-config.spec';
+import {ServiceInjector} from '@app/classes/service-injector';
+import {GraphLegendComponent} from '@app/components/graph-legend/graph-legend.component';
+import {GraphLegendItemComponent} from '@app/components/graph-legend-item/graph-legend-item.component';
+import {GraphTooltipComponent} from '@app/components/graph-tooltip/graph-tooltip.component';
+import {UtilsService} from '@app/services/utils.service';
+
+import {HorizontalHistogramComponent} from './horizontal-histogram.component';
+
+describe('HorizontalHistogramComponent', () => {
+ let component: HorizontalHistogramComponent;
+ let fixture: ComponentFixture<HorizontalHistogramComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ HorizontalHistogramComponent,
+ GraphLegendComponent,
+ GraphLegendItemComponent,
+ GraphTooltipComponent
+ ],
+ imports: [
+ ...TranslationModules
+ ],
+ providers: [
+ UtilsService
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(inject([Injector], (injector: Injector) => {
+ ServiceInjector.injector = injector;
+ fixture = TestBed.createComponent(HorizontalHistogramComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ }));
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts
new file mode 100644
index 0000000..9553e2e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/horizontal-histogram/horizontal-histogram.component.ts
@@ -0,0 +1,114 @@
+/**
+ * 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 {Component, Input} from '@angular/core';
+import * as d3 from 'd3';
+import {GraphComponent} from '@app/classes/components/graph/graph.component';
+import {HomogeneousObject} from '@app/classes/object';
+
+@Component({
+ selector: 'horizontal-histogram',
+ templateUrl: './horizontal-histogram.component.html',
+ styleUrls: ['../../classes/components/graph/graph.component.less', './horizontal-histogram.component.less']
+})
+export class HorizontalHistogramComponent extends GraphComponent {
+
+ /**
+ * Thickness of horizontal bar o the graph
+ * @type {number}
+ */
+ @Input()
+ barSize: number = 5;
+
+ rowsCount: number;
+
+ readonly reverseYRange: boolean = true;
+
+ protected populate(): void {
+ const barSize = this.barSize,
+ data = this.data,
+ yValues = Object.keys(data),
+ keys = Object.keys(this.labels),
+ rowsCount = yValues.reduce((currentCount: number, currentKey: string): number => {
+ return currentCount + Object.keys(this.data[currentKey]).length;
+ }, 0),
+ formattedData = yValues.reduce((currentData, currentKey: string) => {
+ const currentValues = data[currentKey],
+ currentObjects = keys.map((key: string): HomogeneousObject<number> => {
+ return {
+ [key]: currentValues[key] || 0
+ };
+ });
+ return [...currentData, Object.assign({
+ tick: currentKey
+ }, ...currentObjects)];
+ }, []),
+ layers = d3.stack().keys(keys)(formattedData),
+ formattedLayers = d3.transpose<any>(layers);
+
+ this.rowsCount = rowsCount;
+
+ this.setXScaleDomain();
+ this.setYScaleDomain();
+
+ // drawing the axis
+ this.drawXAxis();
+ this.drawYAxis(rowsCount);
+
+ let i = 0;
+
+ // populate the data and drawing the bars
+ this.svg.selectAll().data(formattedLayers).enter().append('g').attr('class', 'value')
+ .selectAll().data(item => item).enter().append('rect')
+ .attr('x', item => this.xScale(0) + 1).attr('y', item => {
+ if (item [0] !== item[1]) {
+ return this.yScale(i++) - this.barSize / 2;
+ }
+ }).attr('height', item => item[0] === item[1] ? '0' : barSize.toString())
+ .attr('width', item => this.xScale(item[1]) - this.xScale(item[0]))
+ .style('fill', (item, index) => this.orderedColors[index])
+ .on('mouseover', this.handleRectMouseOver)
+ .on('mousemove', this.handleRectMouseMove)
+ .on('mouseout', this.handleRectMouseOut);
+ }
+
+ protected setXScaleDomain(): void {
+ const keys = Object.keys(this.data),
+ maxValues = keys.map((currentKey: string): number => this.utils.getMaxNumberInObject(this.data[currentKey]), 0),
+ maximum = Math.max(...maxValues);
+ this.xScale.domain([0, maximum]);
+ }
+
+ protected setYScaleDomain(): void {
+ this.yScale.domain([0, this.rowsCount]);
+ }
+
+ protected yAxisTickFormatter = (tick: any, index: number): string | undefined => {
+ const data = this.data,
+ keys = Object.keys(data);
+ let currentIndex = 0;
+ for (let i = 0; i < keys.length && i <= index; i++) {
+ const currentKey = keys[i];
+ if (currentIndex === index) {
+ return currentKey;
+ } else {
+ currentIndex += Object.keys(data[currentKey]).length;
+ }
+ }
+ };
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 d1b11e6..8e507ae 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
@@ -41,14 +41,14 @@
}}</div>
</div>
<collapsible-panel openTitle="logs.hideGraph" collapsedTitle="logs.showGraph">
- <time-histogram [data]="histogramData" [customOptions]="histogramOptions" svgId="service-logs-histogram"
+ <time-histogram [data]="histogramData" [colors]="serviceLogsHistogramColors" svgId="service-logs-histogram"
(selectArea)="setCustomTimeRange($event[0], $event[1])"></time-histogram>
</collapsible-panel>
<ng-container [ngSwitch]="logsType">
<service-logs-table *ngSwitchCase="'serviceLogs'" [totalCount]="totalCount" [logs]="serviceLogs | async"
[columns]="serviceLogsColumns | async" [filtersForm]="filtersForm"></service-logs-table>
- <audit-logs-table *ngSwitchCase="'auditLogs'" [totalCount]="totalCount" [logs]="auditLogs | async"
- [columns]="auditLogsColumns | async" [filtersForm]="filtersForm"></audit-logs-table>
+ <audit-logs-entries *ngSwitchCase="'auditLogs'" [totalCount]="totalCount" [logs]="auditLogs | async"
+ [columns]="auditLogsColumns | async" [filtersForm]="filtersForm"></audit-logs-entries>
</ng-container>
<log-context *ngIf="isServiceLogContextView" [id]="activeLog.id" [hostName]="activeLog.host_name"
[componentName]="activeLog.component_name"></log-context>
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 cf28a8b..6d50a17 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
@@ -28,8 +28,8 @@ import {ServiceLog} from '@app/classes/models/service-log';
import {Tab} from '@app/classes/models/tab';
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 {HomogeneousObject} from '@app/classes/object';
import {LogsType} from '@app/classes/string';
import {FiltersPanelComponent} from "@app/components/filters-panel/filters-panel.component";
@@ -74,11 +74,11 @@ export class LogsContainerComponent {
return this.logsContainer.totalCount;
}
- histogramData: {[key: string]: number};
+ histogramData: HomogeneousObject<HomogeneousObject<number>>;
- readonly histogramOptions: HistogramOptions = {
- keysWithColors: this.logsContainer.colors
- };
+ get serviceLogsHistogramColors(): HomogeneousObject<string> {
+ return this.logsContainer.colors;
+ }
get autoRefreshRemainingSeconds(): number {
return this.logsContainer.autoRefreshRemainingSeconds;
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
index 3836e7a..67d9423 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
@@ -16,9 +16,10 @@
* limitations under the License.
*/
-import {NO_ERRORS_SCHEMA} from '@angular/core';
-import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {NO_ERRORS_SCHEMA, Injector} from '@angular/core';
+import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {TranslationModules} from '@app/test-config.spec';
+import {ServiceInjector} from '@app/classes/service-injector';
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';
@@ -97,11 +98,12 @@ describe('MenuButtonComponent', () => {
.compileComponents();
}));
- beforeEach(() => {
+ beforeEach(inject([Injector], (injector: Injector) => {
+ ServiceInjector.injector = injector;
fixture = TestBed.createComponent(MenuButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
- });
+ }));
it('should create component', () => {
expect(component).toBeTruthy();
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
index ca89935..12da4ac 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
@@ -18,6 +18,7 @@
import {Component, Input, ViewChild, ElementRef} from '@angular/core';
import {ListItem} from '@app/classes/list-item';
+import {ServiceInjector} from '@app/classes/service-injector';
import {ComponentActionsService} from '@app/services/component-actions.service';
@Component({
@@ -27,7 +28,8 @@ import {ComponentActionsService} from '@app/services/component-actions.service';
})
export class MenuButtonComponent {
- constructor(protected actions: ComponentActionsService) {
+ constructor() {
+ this.actions = ServiceInjector.injector.get(ComponentActionsService);
}
@ViewChild('dropdown')
@@ -82,6 +84,8 @@ export class MenuButtonComponent {
@Input()
maxLongClickDelay: number = 0;
+ private actions: ComponentActionsService;
+
/**
* This is a private property to indicate the mousedown timestamp, so that we can check it when teh click event
* has been triggered.
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 b2136f4..9dd8e26 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
@@ -21,6 +21,7 @@ import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {Subject} from 'rxjs/Subject';
import {SearchBoxParameter, SearchBoxParameterProcessed, SearchBoxParameterTriggered} from '@app/classes/filtering';
import {ListItem} from '@app/classes/list-item';
+import {HomogeneousObject} from '@app/classes/object';
import {UtilsService} from '@app/services/utils.service';
@Component({
@@ -88,7 +89,7 @@ export class SearchBoxComponent implements OnInit, OnDestroy, ControlValueAccess
items: ListItem[] = [];
@Input()
- itemsOptions: {[key: string]: ListItem[]} = {};
+ itemsOptions: HomogeneousObject<ListItem[]> = {};
/**
* Name of parameter to be used if there are no matching values
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
index 9f38371..84a59b9 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
@@ -18,7 +18,7 @@
import {Component, AfterViewInit, ViewChild, ElementRef} from '@angular/core';
import {ListItem} from '@app/classes/list-item';
-import {LogsTableComponent} from '@app/classes/components/logs-table-component';
+import {LogsTableComponent} from '@app/classes/components/logs-table/logs-table-component';
import {LogsContainerService} from '@app/services/logs-container.service';
import {UtilsService} from '@app/services/utils.service';
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.spec.ts
index 2df5090..7eef0e1 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.spec.ts
@@ -47,7 +47,6 @@ describe('TabsComponent', () => {
let activeTab;
const tab = {
id: 'tab0',
- type: '',
isActive: true,
label: '',
appState: null
@@ -64,21 +63,18 @@ describe('TabsComponent', () => {
const items = [
{
id: 'serviceLogs',
- type: '',
isActive: false,
label: '',
appState: null
},
{
id: 'auditLogs',
- type: '',
isActive: false,
label: '',
appState: null
},
{
id: 'newTab',
- type: '',
isActive: false,
label: '',
appState: null
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.ts
index ef941e6..e202c2d 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/tabs/tabs.component.ts
@@ -35,6 +35,7 @@ export class TabsComponent {
tabClosed: EventEmitter<Tab[]> = new EventEmitter();
switchTab(tab: Tab): void {
+ this.items.forEach((item: Tab) => item.isActive = item.id === tab.id);
this.tabSwitched.emit(tab);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
index 1193b2e..720f55e 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
@@ -18,30 +18,20 @@
<header>
<div class="container-fluid">
<div class="row">
- <div *ngIf="chartTimeGap" class="time-gap col-lg-2 col-md-offset-5">
+ <div *ngIf="isTimeGraph && chartTimeGap" class="time-gap col-md-2 col-md-offset-5">
{{chartTimeGap.value}} {{chartTimeGap.label | translate}} {{'histogram.gap' | translate}}
</div>
- <div class="legends col-md-5" [class.md-offset-7]="!chartTimeGap">
- <div *ngFor="let legend of legends" class="legend {{legend.level | lowercase}}">
- {{ legend.label | translate }}
- </div>
- </div>
+ <graph-legend [ngClass]="{'col-md-5 text-right': true, 'md-offset-7': !chartTimeGap}"
+ [items]="legendItems"></graph-legend>
</div>
</div>
</header>
-<div #container></div>
-<footer *ngIf="firstDateTick || lastDateTick">
- <div *ngIf="firstDateTick" class="first-date-tick-label">{{firstDateTick | amTz:timeZone | amDateFormat:historyStartEndTimeFormat}}</div>
- <div *ngIf="lastDateTick" class="last-date-tick-label">{{lastDateTick | amTz:timeZone | amDateFormat:historyStartEndTimeFormat}}</div>
+<div #graphContainer></div>
+<footer *ngIf="isTimeGraph && (firstDateTick || lastDateTick)">
+ <div *ngIf="firstDateTick">{{firstDateTick | amTz: timeZone | amDateFormat: historyStartEndTimeFormat}}</div>
+ <div *ngIf="lastDateTick">{{lastDateTick | amTz: timeZone | amDateFormat: historyStartEndTimeFormat}}</div>
</footer>
-<div [ngClass]="{hide: !tooltipInfo, 'tooltip-left': tooltipOnTheLeft, 'tooltip-chart': true}" #tooltipEl
- [style.top]="tooltipInfo ? (tooltipPosition.top + 'px') : ''" [style.left]="tooltipInfo ? (tooltipPosition.left + 'px') : ''">
- <ng-container *ngIf="tooltipInfo">
- <div class="tooltip-chart-date">{{tooltipInfo.timeStamp | amTz:timeZone | amDateFormat:tickTimeFormat}}</div>
- <div *ngFor="let data of tooltipInfo.data" class="level {{data.level | lowercase}}">
- <span class="level-label">{{data.levelLabel | translate }}</span>
- <span class="level-value">{{data.value}}</span>
- </div>
- </ng-container>
-</div>
-
+<graph-tooltip #tooltip [data]="tooltipInfo.data" [ngClass]="{'hide': !tooltipInfo.data}"
+ [style.top]="tooltipInfo.data ? tooltipPosition.top + 'px' : ''"
+ [style.left]="tooltipInfo.data ? tooltipPosition.left + 'px' : ''"
+ [title]="tooltipInfo.title | amTz: timeZone | amDateFormat: tickTimeFormat"></graph-tooltip>
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
index 1d3766d..364517c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
@@ -20,28 +20,11 @@
:host {
position: relative;
- .level-mixin(@level, @size: .8em) {
- @name: "@{level}-color";
- border-radius: 100%;
- content: "";
- display: inline-block;
- height: .8em;
- width: .8em;
- background-color: @@name;
- }
-
background: #ECECEC; // TODO add style according to actual design
- display: block;
/deep/ .axis {
- .domain {
- display: none;
- }
.tick {
cursor: default;
- line {
- display: none;
- }
}
}
@@ -49,137 +32,21 @@
cursor: crosshair;
}
- /deep/ .value {
- cursor: pointer;
- rect {
- transition: opacity 250ms;
- opacity: .8;
- &:hover {
- opacity: 1;
- }
- }
- }
-
- /deep/ .tooltip-chart {
- background: #fff;
- border-radius: 4px;
- border: @input-border;
- display: block;
- font-size: .8em;
- margin: 0 1.5em;
- min-height: 2em;
- min-width: 5em;
- padding: .5em;
- position: absolute;
- &:empty {
- display: none;
- }
- &::before {
- .caret-mixin(6px, left, #fff);
- left: -6px;
- position: absolute;
- top: calc(50% - 2px);
- }
- &.tooltip-left::before {
- display: none;
- }
- &.tooltip-left::after {
- .caret-mixin(6px, right, #fff);
- right: -6px;
- position: absolute;
- top: calc(50% - 2px);
- }
- .tooltip-chart-date {
- padding: 0 0 .1em 0;
- text-align: center;
- }
- .level {
- display: flex;
- &::before {
- margin: auto .2em auto 0;
- }
- .level-label {
- flex-grow: 3;
- padding: 0 2em 0 0;
- }
- .level-value {
- text-align: right;
- }
- }
-
- .fatal::before {
- .level-mixin('fatal');
- }
- .error::before {
- .level-mixin('error');
- }
- .warn::before {
- .level-mixin('warning');
- }
- .info::before {
- .level-mixin('info');
- }
- .trace::before {
- .level-mixin('trace');
- }
- .debug::before {
- .level-mixin('debug');
- }
- .unknown::before {
- .level-mixin('unknown');
- }
- }
header {
- padding: .5rem;
- }
- .legends {
- text-align: right;
- .legend {
- display: inline-block;
- font-size: 1rem;
- text-transform: uppercase;
- padding-right: 1em;
- }
- .fatal::before {
- .level-mixin('fatal');
- }
- .error::before {
- .level-mixin('error');
- }
- .warn::before {
- .level-mixin('warning');
- }
- .info::before {
- .level-mixin('info');
- }
- .trace::before {
- .level-mixin('trace');
- }
- .debug::before {
- .level-mixin('debug');
- }
- .unknown::before {
- .level-mixin('unknown');
- }
+ padding: @graph-padding;
}
.time-gap {
- color: #666;
+ color: @base-font-color;
font-size: 1.2rem;
text-align: center;
}
footer {
- display: flex;
- div {
- color: #666;
- flex-grow: 1;
- font-size: 1.2rem;
- padding: 0 1em .5em 1em;
- }
- .last-date-tick-label {
- text-align: right;
- }
+ .default-flex;
+ font-size: 1.2rem;
+ color: @base-font-color;
+ padding: 0 1em .5em;
}
/deep/ rect.drag-area {
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
index ee14780..09cd5d8 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
@@ -16,34 +16,41 @@
* limitations under the License.
*/
-import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {Injector} from '@angular/core';
+import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
import {StoreModule} from '@ngrx/store';
import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
import {TranslationModules} from '@app/test-config.spec';
import {MomentModule} from 'angular2-moment';
import {MomentTimezoneModule} from 'angular-moment-timezone';
+import {ServiceInjector} from '@app/classes/service-injector';
import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe';
+import {GraphLegendComponent} from '@app/components/graph-legend/graph-legend.component';
+import {GraphLegendItemComponent} from '@app/components/graph-legend-item/graph-legend-item.component';
+import {GraphTooltipComponent} from '@app/components/graph-tooltip/graph-tooltip.component';
import {ServiceLogsHistogramDataService} from '@app/services/storage/service-logs-histogram-data.service';
import {TimeHistogramComponent} from './time-histogram.component';
import {LogsContainerService} from '@app/services/logs-container.service';
-import {HttpClientService} from "@app/services/http-client.service";
-import {AppStateService} from "@app/services/storage/app-state.service";
-import {AuditLogsService} from "@app/services/storage/audit-logs.service";
-import {AuditLogsFieldsService} from "@app/services/storage/audit-logs-fields.service";
-import {ServiceLogsService} from "@app/services/storage/service-logs.service";
-import {ServiceLogsFieldsService} from "@app/services/storage/service-logs-fields.service";
-import {ServiceLogsTruncatedService} from "@app/services/storage/service-logs-truncated.service";
-import {TabsService} from "@app/services/storage/tabs.service";
-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 {HttpClientService} from '@app/services/http-client.service';
+import {UtilsService} from '@app/services/utils.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
+import {AuditLogsService} from '@app/services/storage/audit-logs.service';
+import {AuditLogsFieldsService} from '@app/services/storage/audit-logs-fields.service';
+import {ServiceLogsService} from '@app/services/storage/service-logs.service';
+import {ServiceLogsFieldsService} from '@app/services/storage/service-logs-fields.service';
+import {ServiceLogsTruncatedService} from '@app/services/storage/service-logs-truncated.service';
+import {TabsService} from '@app/services/storage/tabs.service';
+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 {HomogeneousObject} from '@app/classes/object';
describe('TimeHistogramComponent', () => {
let component: TimeHistogramComponent;
let fixture: ComponentFixture<TimeHistogramComponent>;
let histogramData: any;
- let customOptions: any;
+ let colors: HomogeneousObject<string>;
beforeEach(async(() => {
const httpClient = {
@@ -54,29 +61,42 @@ describe('TimeHistogramComponent', () => {
}
};
histogramData = {
- "1512476481940": {
- "FATAL": 0,
- "ERROR": 1000,
- "WARN": 700,
- "INFO": 0,
- "DEBUG": 0,
- "TRACE": 0,
- "UNKNOWN": 0
- }, "1512472881940": {"FATAL": 0, "ERROR": 2000, "WARN": 900, "INFO": 0, "DEBUG": 0, "TRACE": 0, "UNKNOWN": 0}
- };
- customOptions = {
- keysWithColors: {
- FATAL: '#830A0A',
- ERROR: '#E81D1D',
- WARN: '#FF8916',
- INFO: '#2577B5',
- DEBUG: '#65E8FF',
- TRACE: '#888',
- UNKNOWN: '#BDBDBD'
+ 1512476481940: {
+ FATAL: 0,
+ ERROR: 1000,
+ WARN: 700,
+ INFO: 0,
+ DEBUG: 0,
+ TRACE: 0,
+ UNKNOWN: 0
+ },
+ 1512472881940: {
+ FATAL: 0,
+ ERROR: 2000,
+ WARN: 900,
+ INFO: 0,
+ DEBUG: 0,
+ TRACE: 0,
+ UNKNOWN: 0
}
};
+ colors = {
+ FATAL: '#830A0A',
+ ERROR: '#E81D1D',
+ WARN: '#FF8916',
+ INFO: '#2577B5',
+ DEBUG: '#65E8FF',
+ TRACE: '#888',
+ UNKNOWN: '#BDBDBD'
+ };
TestBed.configureTestingModule({
- declarations: [TimeHistogramComponent, TimeZoneAbbrPipe],
+ declarations: [
+ TimeHistogramComponent,
+ GraphLegendComponent,
+ GraphLegendItemComponent,
+ GraphTooltipComponent,
+ TimeZoneAbbrPipe
+ ],
imports: [
StoreModule.provideStore({
appSettings
@@ -93,6 +113,7 @@ describe('TimeHistogramComponent', () => {
provide: HttpClientService,
useValue: httpClient
},
+ UtilsService,
AppStateService,
AuditLogsService,
AuditLogsFieldsService,
@@ -109,14 +130,15 @@ describe('TimeHistogramComponent', () => {
.compileComponents();
}));
- beforeEach(() => {
- fixture = TestBed.createComponent(TimeHistogramComponent);
- component = fixture.componentInstance;
- component.customOptions = customOptions;
- component.svgId = "HistogramSvg";
- component.data = histogramData;
- fixture.detectChanges();
- });
+ beforeEach(inject([Injector], (injector: Injector) => {
+ ServiceInjector.injector = injector;
+ fixture = TestBed.createComponent(TimeHistogramComponent);
+ component = fixture.componentInstance;
+ component.colors = colors;
+ component.svgId = 'HistogramSvg';
+ component.data = histogramData;
+ fixture.detectChanges();
+ }));
it('should create component', () => {
expect(component).toBeTruthy();
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
index fb3092f..8544677 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
@@ -16,100 +16,48 @@
* limitations under the License.
*/
-import {Component, OnInit, AfterViewInit, OnChanges, Input, Output, ViewChild, ElementRef, EventEmitter} from '@angular/core';
-import {ContainerElement, Selection} from 'd3';
+import {Component, OnInit, Input, Output, EventEmitter} from '@angular/core';
import * as d3 from 'd3';
import * as moment from 'moment-timezone';
import {AppSettingsService} from '@app/services/storage/app-settings.service';
-import {HistogramStyleOptions, HistogramOptions} from '@app/classes/histogram-options';
+import {GraphComponent} from '@app/classes/components/graph/graph.component';
+import {GraphScaleItem} from '@app/classes/graph';
@Component({
selector: 'time-histogram',
templateUrl: './time-histogram.component.html',
- styleUrls: ['./time-histogram.component.less']
+ styleUrls: ['../../classes/components/graph/graph.component.less', './time-histogram.component.less']
})
-export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges {
+export class TimeHistogramComponent extends GraphComponent implements OnInit {
- constructor(private appSettings: AppSettingsService) {}
+ constructor(private appSettings: AppSettingsService) {
+ super();
+ }
ngOnInit() {
this.appSettings.getParameter('timeZone').subscribe((value: string): void => {
this.timeZone = value;
- this.createHistogram();
+ this.createGraph();
});
- this.options = Object.assign({}, this.defaultOptions, this.customOptions);
- }
-
- ngAfterViewInit() {
- this.htmlElement = this.element.nativeElement;
- this.tooltipElement = this.tooltipEl.nativeElement;
- this.host = d3.select(this.htmlElement);
- }
-
- ngOnChanges() {
- this.createHistogram();
}
- @ViewChild('container')
- element: ElementRef;
-
- @ViewChild('tooltipEl')
- tooltipEl: ElementRef;
-
- @Input()
- svgId: string;
-
@Input()
- customOptions: HistogramOptions;
-
- @Input()
- data: {[key: string]: number};
+ columnWidth = {
+ second: 40,
+ minute: 30,
+ hour: 25,
+ day: 20,
+ base: 20
+ };
@Output()
selectArea: EventEmitter<number[]> = new EventEmitter();
- private readonly defaultOptions: HistogramStyleOptions = {
- margin: {
- top: 5,
- right: 50,
- bottom: 30,
- left: 50
- },
- height: 150,
- tickPadding: 10,
- columnWidth: {
- second: 40,
- minute: 30,
- hour: 25,
- day: 20,
- base: 20
- }
- };
-
- private options: HistogramOptions;
+ readonly isTimeGraph: boolean = true;
private timeZone: string;
- private host;
-
- private svg;
-
- private width: number;
-
- private xScale;
-
- private yScale;
-
- private colorScale;
-
- private xAxis;
-
- private yAxis;
-
- private htmlElement: HTMLElement;
- private tooltipElement: HTMLElement;
-
- private dragArea: Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
+ private dragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
private dragStartX: number;
@@ -118,26 +66,12 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
private maxDragX: number;
private readonly tickTimeFormat: string = 'MM/DD HH:mm';
- private readonly historyStartEndTimeFormat = 'dddd, MMMM DD, YYYY';
+
+ private readonly historyStartEndTimeFormat: string = 'dddd, MMMM DD, YYYY';
histogram: any;
/**
- * This property is to hold the data of the bar where the mouse is over.
- */
- private tooltipInfo: {data: object, timeStamp: number};
- /**
- * This is the computed position of the tooltip relative to the @htmlElement which is the container of the histogram.
- * It is set when the mousemoving over the bars in the @handleRectMouseMove method.
- */
- private tooltipPosition: {top: number, left: number};
- /**
- * This property indicates if the tooltip should be positioned on the left side of the cursor or not.
- * It should be true when the tooltip is out from the window.
- * @type {boolean}
- */
- private tooltipOnTheLeft: boolean = false;
- /**
* This property holds the data structure describing the gaps between the xAxis ticks.
* The unit property can be: second, minute, hour, day
* The value is the number of the given unit.
@@ -146,11 +80,11 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
/**
* This is the rectangle element to represent the unselected time range on the left side of the selected time range
*/
- private leftDragArea: Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
+ private leftDragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
/**
* This is the rectangle element to represent the unselected time range on the right side of the selected time range
*/
- private rightDragArea: Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
+ private rightDragArea: d3.Selection<SVGGraphicsElement, undefined, SVGGraphicsElement, undefined>;
/**
* This is a Date object holding the value of the first tick of the xAxis. It is a helper getter for the template.
*/
@@ -166,134 +100,12 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
return (ticks && ticks.length && ticks[ticks.length-1]) || undefined;
}
- /**
- * This will return the information about the used levels and the connected colors and labels.
- * The goal is to provide an easy property to the template to display the legend of the levels.
- * @returns {Array<{level: string; label: string; color: string}>}
- */
- private get legends(): Array<{level: string, label: string, color: string}> {
- return Object.keys(this.options.keysWithColors).map(level => Object.assign({},{
- level,
- label: `levels.${level.toLowerCase()}`,
- color: this.options.keysWithColors[level]
- }));
- }
-
- private createHistogram(): void {
- if (this.host) {
- this.setup();
- this.buildSVG();
- this.populate();
- }
- }
-
- private setup(): void {
- const margin = this.options.margin,
- keysWithColors = this.options.keysWithColors,
- keys = Object.keys(keysWithColors),
- colors = keys.reduce((array: string[], key: string): string[] => [...array, keysWithColors[key]], []);
- this.width = this.htmlElement.clientWidth - margin.left - margin.right;
- this.xScale = d3.scaleTime().range([0, this.width]);
- this.yScale = d3.scaleLinear().range([this.options.height, 0]);
- this.colorScale = d3.scaleOrdinal(colors);
- }
-
- private buildSVG(): void {
- const margin = this.options.margin;
- this.host.html('');
- this.svg = this.host.append('svg').attr('id', this.svgId).attr('width', this.htmlElement.clientWidth)
- .attr('height', this.options.height + margin.top + margin.bottom).append('g')
- .attr('transform', `translate(${margin.left},${margin.top})`);
- }
-
- /**
- * It draws the svg representation of the x axis. The goal is to set the ticks here, add the axis to the svg element
- * and set the position of the axis.
- */
- private drawXAxis(): void {
- this.xAxis = d3.axisBottom(this.xScale)
- .tickFormat(tick => moment(tick).tz(this.timeZone).format(this.tickTimeFormat))
- .tickPadding(this.options.tickPadding);
- this.svg.append('g').attr('class', 'axis axis-x').attr('transform', `translate(0,${this.options.height})`).call(this.xAxis);
- }
-
- /**
- * It draws the svg representation of the y axis. The goal is to set the ticks here, add the axis to the svg element
- * and set the position of the axis.
- */
- private drawYAxis(): void {
- this.yAxis = d3.axisLeft(this.yScale).tickFormat((tick: number): string | undefined => {
- if (Number.isInteger(tick)) {
- return tick.toFixed(0);
- } else {
- return;
- }
- }).tickPadding(this.options.tickPadding);
- this.svg.append('g').attr('class', 'axis axis-y').call(this.yAxis).append('text');
- };
-
- /**
- * The goal is to handle the mouse over event on the rect svg elements so that we can populate the tooltip info object
- * and set the initial position of the tooltip. So we call the corresponding methods.
- * @param d The data for the currently "selected" bar
- * @param {number} index The index of the current element in the selection
- * @param elements The selection of the elements
- */
- private handleRectMouseOver = (d: any, index: number, elements: any):void => {
- this.setTooltipDataFromChartData(d);
- this.setTooltipPosition();
- };
-
- /**
- * The goal is to handle the movement of the mouse over the rect svg elements, so that we can set the position of
- * the tooltip by calling the @setTooltipPosition method.
- */
- private handleRectMouseMove = ():void => {
- this.setTooltipPosition();
+ protected xAxisTickFormatter = (tick: Date): string => {
+ return moment(tick).tz(this.timeZone).format(this.tickTimeFormat);
};
- /**
- * The goal is to reset the tooltipInfo object so that the tooltip will be hidden.
- */
- private handleRectMouseOut = ():void => {
- this.tooltipInfo = null;
- };
-
- /**
- * The goal is set the tooltip
- * @param d
- */
- private setTooltipDataFromChartData(d: {data: any, [key: string]: any}): void {
- let {timeStamp, ...data} = d.data;
- let levelColors = this.options.keysWithColors;
- this.tooltipInfo = {
- data: Object.keys(levelColors).map(key => Object.assign({}, {
- level: key,
- levelLabel: `levels.${key.toLowerCase()}`,
- value: data[key]
- })),
- timeStamp
- };
- }
-
- /**
- * The goal of this function is to set the tooltip position regarding the d3.mouse event relative to the @htmlElement.
- * Onlty if we have @tooltipInfo
- */
- private setTooltipPosition():void {
- if (this.tooltipInfo) {
- let tEl = this.tooltipElement;
- let pos = d3.mouse(this.htmlElement);
- let left = pos[0];
- let top = pos[1] - (tEl.offsetHeight / 2);
- let tooltipWidth = tEl.offsetWidth;
- let windowSize = window.innerWidth;
- if (left + tooltipWidth > windowSize) {
- left = pos[0] - (tooltipWidth + 25);
- }
- this.tooltipOnTheLeft = left < pos[0];
- this.tooltipPosition = {left, top};
- }
+ protected yAxisTickFormatter = (tick: number): string | undefined => {
+ return Number.isInteger(tick) ? tick.toFixed(0) : undefined;
};
/**
@@ -361,33 +173,33 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
/**
* Set the domain for the y scale regarding the given data. The maximum value of the data is the sum of the log level
* values.
- * An example data: [{timeStamp: 1233455677, WARN: 12, ERROR: 123}]
- * @param {Array<{timeStamp: number; [p: string]: number}>} data
+ * An example data: [{tick: 1233455677, WARN: 12, ERROR: 123}]
+ * @param {GraphScaleItem[]} data
*/
- private setYScaleDomain(data: Array<{timeStamp: number, [key: string]: number}>): void {
- const keys = Object.keys(this.options.keysWithColors);
+ protected setYScaleDomain(data: GraphScaleItem[]): void {
+ const keys = Object.keys(this.labels);
const maxYValue = d3.max(data, item => keys.reduce((sum: number, key: string): number => sum + item[key], 0));
this.yScale.domain([0, maxYValue]);
}
/**
* Set the domain values for the x scale regarding the given data.
- * An example data: [{timeStamp: 1233455677, WARN: 12, ERROR: 123}]
- * @param {Array<{timeStamp: number; [p: string]: any}>} data
+ * An example data: [{tick: 1233455677, WARN: 12, ERROR: 123}]
+ * @param {GraphScaleItem[]} data
*/
- private setXScaleDomain(data: Array<{timeStamp: number, [key: string]: any}>): void {
- this.xScale.domain(d3.extent(data, item => item.timeStamp)).nice();
+ protected setXScaleDomain(data: GraphScaleItem[]): void {
+ this.xScale.domain(d3.extent(data, item => item.tick)).nice();
}
- private populate(): void {
- const keys = Object.keys(this.options.keysWithColors);
+ protected populate(): void {
+ const keys = Object.keys(this.colors);
const data = this.data;
const timeStamps = Object.keys(data);
// we create a more consumable data structure for d3
- const formattedData = timeStamps.map((timeStamp: string): {timeStamp: number, [key: string]: number} => Object.assign({
- timeStamp: Number(timeStamp)
+ const formattedData = timeStamps.map((timeStamp: string): {tick: number, [key: string]: number} => Object.assign({
+ tick: Number(timeStamp)
}, data[timeStamp]));
- const layers = (d3.stack().keys(keys)(formattedData));
+ const layers = d3.stack().keys(keys)(formattedData);
// after we have the data we set the domain values both scales
this.setXScaleDomain(formattedData);
@@ -396,39 +208,32 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
// Setting the timegap label above the chart
this.setChartTimeGapByXScale();
- let unitD3TimeProp = this.chartTimeGap.unit.charAt(0).toUpperCase() + this.chartTimeGap.unit.slice(1);
+ const unitD3TimeProp = this.chartTimeGap.unit.charAt(0).toUpperCase() + this.chartTimeGap.unit.slice(1);
this.xScale.nice(d3[`time${unitD3TimeProp}`], 2);
- let columnWidth = this.options.columnWidth[this.chartTimeGap.unit] || this.options.columnWidth.base;
+ const columnWidth = this.columnWidth[this.chartTimeGap.unit] || this.columnWidth.base;
// drawing the axis
this.drawXAxis();
this.drawYAxis();
// populate the data and drawing the bars
- const layer = this.svg.selectAll('.value').data(d3.transpose<any>(layers))
- .attr('class', 'value')
- .enter().append('g')
- .attr('class', 'value');
- layer.selectAll('.value rect').data(item => item)
- .attr('x', item => this.xScale(item.data.timeStamp) - columnWidth / 2)
- .attr('y', item => this.yScale(item[1]))
- .attr('height', item => this.yScale(item[0]) - this.yScale(item[1]))
- .attr('width', columnWidth.toString())
- .style('fill', (item, index) => this.colorScale(index))
- .enter().append('rect')
- .attr('x', item => this.xScale(item.data.timeStamp) - columnWidth / 2)
+ const layer = this.svg.selectAll().data(d3.transpose<any>(layers))
+ .enter().append('g')
+ .attr('class', 'value');
+ layer.selectAll().data(item => item).enter().append('rect')
+ .attr('x', item => this.xScale(item.data.tick) - columnWidth / 2)
.attr('y', item => this.yScale(item[1]))
.attr('height', item => this.yScale(item[0]) - this.yScale(item[1]))
.attr('width', columnWidth.toString())
- .style('fill', (item, index) => this.colorScale(index))
+ .style('fill', (item, index) => this.orderedColors[index])
.on('mouseover', this.handleRectMouseOver)
.on('mousemove', this.handleRectMouseMove)
.on('mouseout', this.handleRectMouseOut);
this.setDragBehavior();
}
- private getTimeRangeByXRanges(startX: number, endX:number): [number, number] {
+ private getTimeRangeByXRanges(startX: number, endX: number): [number, number] {
const xScaleInterval = this.xScale.domain().map((point: Date): number => point.valueOf());
const xScaleLength = xScaleInterval[1] - xScaleInterval[0];
const ratio = xScaleLength / this.width;
@@ -442,7 +247,7 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
* @param {number} currentX This is the ending point of the drag within the container
*/
private createInvertDragArea(startX: number, currentX: number): void {
- const height: number = this.options.height + this.options.margin.top + this.options.margin.bottom;
+ const height: number = this.height + this.margin.top + this.margin.bottom;
this.leftDragArea = this.svg.insert('rect').attr('height', height).attr('class', 'unselected-drag-area');
this.rightDragArea = this.svg.insert('rect').attr('height', height).attr('class', 'unselected-drag-area');
this.setInvertDragArea(startX, currentX);
@@ -456,9 +261,8 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
private setInvertDragArea(startX: number, currentX: number): void {
const left: number = Math.min(startX, currentX);
const right: number = Math.max(startX, currentX);
- let rightAreaWidth: number = this.width - right;
- rightAreaWidth = rightAreaWidth > 0 ? rightAreaWidth : 0;
- let leftAreaWidth: number = left > 0 ? left : 0;
+ const rightAreaWidth: number = Math.max(0, this.width - right);
+ const leftAreaWidth: number = Math.max(0, left);
this.leftDragArea.attr('x', 0).attr('width', leftAreaWidth);
this.rightDragArea.attr('x', right).attr('width', rightAreaWidth);
}
@@ -472,20 +276,20 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
}
private setDragBehavior(): void {
- this.minDragX = this.options.margin.left;
- this.maxDragX = this.htmlElement.clientWidth;
+ this.minDragX = this.margin.left;
+ this.maxDragX = this.graphContainer.clientWidth;
d3.selectAll(`svg#${this.svgId}`).call(d3.drag()
- .on('start', (datum: undefined, index: number, containers: ContainerElement[]): void => {
+ .on('start', (datum: undefined, index: number, containers: d3.ContainerElement[]): void => {
if (this.dragArea) {
this.dragArea.remove();
}
- this.dragStartX = Math.max(0, this.getDragX(containers[0]) - this.options.margin.left);
+ this.dragStartX = Math.max(0, this.getDragX(containers[0]) - this.margin.left);
this.dragArea = this.svg.insert('rect', ':first-child').attr('x', this.dragStartX).attr('y', 0).attr('width', 0)
- .attr('height', this.options.height).attr('class', 'drag-area');
+ .attr('height', this.height).attr('class', 'drag-area');
})
- .on('drag', (datum: undefined, index: number, containers: ContainerElement[]): void => {
+ .on('drag', (datum: undefined, index: number, containers: d3.ContainerElement[]): void => {
const mousePos = this.getDragX(containers[0]);
- const currentX = Math.max(mousePos, this.minDragX) - this.options.margin.left;
+ const currentX = Math.max(mousePos, this.minDragX) - this.margin.left;
const startX = Math.min(currentX, this.dragStartX);
const currentWidth = Math.abs(currentX - this.dragStartX);
this.dragArea.attr('x', startX).attr('width', currentWidth);
@@ -507,7 +311,7 @@ export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges
}));
}
- private getDragX(element: ContainerElement): number {
+ private getDragX(element: d3.ContainerElement): number {
return d3.mouse(element)[0];
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
index 6df7ab3..8d739ec 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
@@ -20,6 +20,7 @@ import {Component} from '@angular/core';
import {FormGroup} from '@angular/forms';
import {FilterCondition, TimeUnitListItem} from '@app/classes/filtering';
import {ListItem} from '@app/classes/list-item';
+import {HomogeneousObject} from '@app/classes/object';
import {LogsContainerService} from '@app/services/logs-container.service';
@Component({
@@ -36,7 +37,7 @@ export class TopMenuComponent {
return this.logsContainer.filtersForm;
};
- get filters(): {[key: string]: FilterCondition} {
+ get filters(): HomogeneousObject<FilterCondition> {
return this.logsContainer.filters;
};
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 a9ca3b4..9b9bbfd 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
@@ -65,3 +65,6 @@
// Table
@table-border-color: #EEE;
+
+// Graph
+@graph-padding: .5rem;
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
index 7578867..aa86f4f 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/mock-data.ts
@@ -18,6 +18,8 @@
import * as moment from 'moment';
+const currentTime = moment();
+
export const mockData = {
login: {},
logout: {},
@@ -153,34 +155,82 @@ export const mockData = {
},
components: {},
resources: {
- graphData: [
- {
- dataCount: [
- {
- name: 'n16',
- value: 800
- },
- {
- name: 'n17',
- value: 400
- }
- ],
- name: 'graph8'
- },
- {
- dataCount: [
- {
- name: 'n18',
- value: 600
- },
- {
- name: 'n19',
- value: 300
- }
- ],
- name: 'graph9'
- }
- ]
+ 6: {
+ graphData: [
+ {
+ dataCount: [
+ {
+ name: 'hdfs',
+ value: 800
+ },
+ {
+ name: 'zookeeper',
+ value: 400
+ },
+ {
+ name: 'ambari_metrics',
+ value: 200
+ }
+ ],
+ name: 'admin'
+ },
+ {
+ dataCount: [
+ {
+ name: 'ambari_agent',
+ value: 400
+ },
+ {
+ name: 'hdfs',
+ value: 600
+ },
+ {
+ name: 'ambari_metrics',
+ value: 300
+ }
+ ],
+ name: 'user'
+ }
+ ]
+ },
+ 10: {
+ graphData: [
+ {
+ dataCount: [
+ {
+ name: 'ambari',
+ value: 800
+ },
+ {
+ name: 'hdfs',
+ value: 400
+ },
+ {
+ name: 'hbase',
+ value: 200
+ },
+ ],
+ name: '/user'
+ },
+ {
+ dataCount: [
+ {
+ name: 'hdfs',
+ value: 400
+ },
+ {
+ name: 'hbase',
+ value: 600
+ },
+ {
+ name: 'kafka',
+ value: 300
+ }
+ ],
+ name: '/root'
+ }
+ ]
+ }
},
schema: {
fields: {
@@ -299,7 +349,7 @@ export const mockData = {
path: '/var/log/ambari-metrics-collector/ambari-metrics-collector.log',
host: 'h0',
level: 'WARN',
- logtime: moment().valueOf(),
+ logtime: currentTime.valueOf(),
ip: '192.168.0.1',
logfile_line_number: 8,
type: 'ams_collector',
@@ -316,14 +366,14 @@ export const mockData = {
event_md5: '1908755391',
event_dur_ms: 200,
_ttl_: '+5DAYS',
- _expire_at_: moment().add(5, 'd').valueOf(),
+ _expire_at_: currentTime.clone().add(5, 'd').valueOf(),
_router_field_: 20
},
{
path: '/var/log/ambari-metrics-collector/ambari-metrics-collector.log',
host: 'h1',
level: 'ERROR',
- logtime: moment().subtract(2, 'd').valueOf(),
+ logtime: currentTime.clone().subtract(2, 'd').valueOf(),
ip: '192.168.0.2',
type: 'ams_collector',
_version_: 14,
@@ -340,14 +390,14 @@ export const mockData = {
event_md5: '1029384756',
event_dur_ms: 700,
_ttl_: '+5DAYS',
- _expire_at_: moment().add(3, 'd').valueOf(),
+ _expire_at_: currentTime.clone().add(3, 'd').valueOf(),
_router_field_: 5
},
{
path: '/var/log/ambari-metrics-collector/ambari-metrics-collector.log',
host: 'h1',
level: 'FATAL',
- logtime: moment().subtract(10, 'd').valueOf(),
+ logtime: currentTime.clone().subtract(10, 'd').valueOf(),
ip: '192.168.0.3',
type: 'ambari_agent',
_version_: 14,
@@ -364,14 +414,14 @@ export const mockData = {
event_md5: '67589403',
event_dur_ms: 100,
_ttl_: '+5DAYS',
- _expire_at_: moment().subtract(5, 'd').valueOf(),
+ _expire_at_: currentTime.clone().subtract(5, 'd').valueOf(),
_router_field_: 45
},
{
path: '/var/log/ambari-metrics-collector/zookeeper-server.log',
host: 'h1',
level: 'INFO',
- logtime: moment().subtract(25, 'h').valueOf(),
+ logtime: currentTime.clone().subtract(25, 'h').valueOf(),
ip: '192.168.0.4',
type: 'zookeeper_server',
_version_: 14,
@@ -388,14 +438,14 @@ export const mockData = {
event_md5: '67589403',
event_dur_ms: 1000,
_ttl_: '+5DAYS',
- _expire_at_: moment().subtract(25, 'h').add(5, 'd').valueOf(),
+ _expire_at_: currentTime.clone().subtract(25, 'h').add(5, 'd').valueOf(),
_router_field_: 55
},
{
path: '/var/log/ambari-metrics-collector/zookeeper-server.log',
host: 'h1',
level: 'DEBUG',
- logtime: moment().subtract(25, 'd').valueOf(),
+ logtime: currentTime.clone().subtract(25, 'd').valueOf(),
ip: '192.168.0.4',
type: 'zookeeper_server',
_version_: 14,
@@ -412,14 +462,14 @@ export const mockData = {
event_md5: '67589403',
event_dur_ms: 1000,
_ttl_: '+5DAYS',
- _expire_at_: moment().subtract(20, 'd').valueOf(),
+ _expire_at_: currentTime.clone().subtract(20, 'd').valueOf(),
_router_field_: 55
},
{
path: '/var/log/ambari-metrics-collector/zookeeper-client.log',
host: 'h1',
level: 'TRACE',
- logtime: moment().subtract(2, 'h').valueOf(),
+ logtime: currentTime.clone().subtract(2, 'h').valueOf(),
ip: '192.168.0.4',
type: 'zookeeper_client',
_version_: 14,
@@ -436,14 +486,14 @@ export const mockData = {
event_md5: '67589403',
event_dur_ms: 1000,
_ttl_: '+5DAYS',
- _expire_at_: moment().subtract(2, 'h').add(5, 'd').valueOf(),
+ _expire_at_: currentTime.clone().subtract(2, 'h').add(5, 'd').valueOf(),
_router_field_: 55
},
{
path: '/var/log/ambari-metrics-collector/zookeeper-client.log',
host: 'h1',
level: 'UNKNOWN',
- logtime: moment().subtract(31, 'd').valueOf(),
+ logtime: currentTime.clone().subtract(31, 'd').valueOf(),
ip: '192.168.0.4',
type: 'zookeeper_client',
_version_: 14,
@@ -460,7 +510,7 @@ export const mockData = {
event_md5: '67589403',
event_dur_ms: 1000,
_ttl_: '+5DAYS',
- _expire_at_: moment().subtract(26, 'd').valueOf(),
+ _expire_at_: currentTime.clone().subtract(26, 'd').valueOf(),
_router_field_: 55
}
],
@@ -637,11 +687,11 @@ export const mockData = {
{
dataCount: [
{
- name: moment().toISOString(),
+ name: currentTime.toISOString(),
value: '1000'
},
{
- name: moment().subtract(1, 'h').toISOString(),
+ name: currentTime.clone().subtract(1, 'h').toISOString(),
value: '2000'
}
],
@@ -650,11 +700,11 @@ export const mockData = {
{
dataCount: [
{
- name: moment().toISOString(),
+ name: currentTime.toISOString(),
value: '700'
},
{
- name: moment().subtract(1, 'h').toISOString(),
+ name: currentTime.clone().subtract(1, 'h').toISOString(),
value: '900'
}
],
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 51b0c0b..36c4d8d 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
@@ -83,7 +83,6 @@ export class ComponentActionsService {
openLog(log: ServiceLog): void {
const tab = {
id: log.id,
- type: 'serviceLogs',
isCloseable: true,
label: `${log.host} >> ${log.type}`,
appState: {
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts b/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts
index 9b61bf6..6f98bf5 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/services/http-client.service.ts
@@ -19,8 +19,12 @@
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/first';
-import {Http, XHRBackend, Request, RequestOptions, RequestOptionsArgs, Response, Headers, URLSearchParams} from '@angular/http';
-import {AuditLogsQueryParams} from '@app/classes/queries/audit-logs-query-params';
+import {
+ Http, XHRBackend, Request, RequestOptions, RequestOptionsArgs, Response, Headers, URLSearchParams
+} from '@angular/http';
+import {HomogeneousObject} from '@app/classes/object';
+import {AuditLogsListQueryParams} from '@app/classes/queries/audit-logs-query-params';
+import {AuditLogsTopResourcesQueryParams} from '@app/classes/queries/audit-logs-top-resources-query-params';
import {ServiceLogsQueryParams} from '@app/classes/queries/service-logs-query-params';
import {ServiceLogsHistogramQueryParams} from '@app/classes/queries/service-logs-histogram-query-params';
import {ServiceLogsTruncatedQueryParams} from '@app/classes/queries/service-logs-truncated-query-params';
@@ -41,7 +45,7 @@ export class HttpClientService extends Http {
},
auditLogs: {
url: 'audit/logs',
- params: opts => new AuditLogsQueryParams(opts)
+ params: opts => new AuditLogsListQueryParams(opts)
},
auditLogsFields: {
url: 'audit/logs/schema/fields'
@@ -69,14 +73,31 @@ export class HttpClientService extends Http {
},
hosts: {
url: 'service/logs/tree'
+ },
+ topAuditLogsResources: {
+ url: variables => `audit/logs/resources/${variables.number}`,
+ params: opts => new AuditLogsTopResourcesQueryParams(opts)
}
};
private readonly unauthorizedStatuses = [401, 403, 419];
- private generateUrlString(url: string): string {
+ private generateUrlString(url: string, urlVariables?: HomogeneousObject<string>): string {
const preset = this.endPoints[url];
- return preset ? `${this.apiPrefix}${preset.url}` : url;
+ let generatedUrl: string;
+ if (preset) {
+ const urlExpression = preset.url;
+ let path: string;
+ if (typeof urlExpression === 'function') {
+ path = preset.url(urlVariables);
+ } else if (typeof urlExpression === 'string') {
+ path = preset.url;
+ }
+ generatedUrl = `${this.apiPrefix}${path}`;
+ } else {
+ generatedUrl = url;
+ }
+ return generatedUrl;
}
private generateUrl(request: string | Request): string | Request {
@@ -89,7 +110,7 @@ export class HttpClientService extends Http {
}
}
- private generateOptions(url: string, params: {[key: string]: string}): RequestOptionsArgs {
+ private generateOptions(url: string, params: HomogeneousObject<string>): RequestOptionsArgs {
const preset = this.endPoints[url],
rawParams = preset && preset.params ? preset.params(params) : params;
if (rawParams) {
@@ -122,11 +143,11 @@ export class HttpClientService extends Http {
return req;
}
- get(url, params?: {[key: string]: string}): Observable<Response> {
- return super.get(this.generateUrlString(url), this.generateOptions(url, params));
+ get(url, params?: HomogeneousObject<string>, urlVariables?: HomogeneousObject<string>): Observable<Response> {
+ return super.get(this.generateUrlString(url, urlVariables), this.generateOptions(url, params));
}
- postFormData(url: string, params: {[key: string]: string}, options?: RequestOptionsArgs): Observable<Response> {
+ postFormData(url: string, params: HomogeneousObject<string>, options?: RequestOptionsArgs): Observable<Response> {
const encodedParams = this.generateOptions(url, params).params;
let body;
if (encodedParams && encodedParams instanceof URLSearchParams) {
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 870058b..acdc0c2 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
@@ -23,7 +23,9 @@ import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.serv
import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
-import {ServiceLogsHistogramDataService, serviceLogsHistogramData} from '@app/services/storage/service-logs-histogram-data.service';
+import {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
import {AppStateService, appState} from '@app/services/storage/app-state.service';
import {ClustersService, clusters} from '@app/services/storage/clusters.service';
http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 d719893..78bcdb1 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,7 +27,6 @@ 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';
@@ -46,6 +45,7 @@ import {
FilterCondition, TimeUnitListItem, SortingListItem, SearchBoxParameter, SearchBoxParameterTriggered
} from '@app/classes/filtering';
import {ListItem} from '@app/classes/list-item';
+import {HomogeneousObject} from '@app/classes/object';
import {LogsType, ScrollType, SortingType} from '@app/classes/string';
import {Tab} from '@app/classes/models/tab';
import {LogField} from '@app/classes/models/log-field';
@@ -61,15 +61,15 @@ import {CommonEntry} from '@app/classes/models/common-entry';
export class LogsContainerService {
constructor(
- 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,
- private componentsStorage: ComponentsService, private hostsStorage: HostsService
+ private httpClient: HttpClientService, private appState: AppStateService,
+ private appSettings: AppSettingsService, private auditLogsStorage: AuditLogsService,
+ private auditLogsFieldsStorage: AuditLogsFieldsService, private serviceLogsStorage: ServiceLogsService,
+ private serviceLogsFieldsStorage: ServiceLogsFieldsService, private tabsStorage: TabsService,
+ private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private clustersStorage: ClustersService,
+ private componentsStorage: ComponentsService, private hostsStorage: HostsService,
+ private serviceLogsTruncatedStorage: ServiceLogsTruncatedService
) {
- const formItems = Object.keys(this.filters).reduce((currentObject: any, key: string): {[key: string]: FormControl} => {
+ const formItems = Object.keys(this.filters).reduce((currentObject: any, key: string): HomogeneousObject<FormControl> => {
let formControl = new FormControl(),
item = {
[key]: formControl
@@ -88,7 +88,7 @@ export class LogsContainerService {
tabsStorage.mapCollection((tab: Tab): Tab => {
let currentAppState = tab.appState || {};
const appState = Object.assign({}, currentAppState, {
- activeFilters: this.getFiltersData(tab.type)
+ activeFilters: this.getFiltersData(tab.appState.activeLogsType)
});
return Object.assign({}, tab, {
appState
@@ -120,7 +120,7 @@ export class LogsContainerService {
private readonly paginationOptions: string[] = ['10', '25', '50', '100'];
- filters: {[key: string]: FilterCondition} = {
+ filters: HomogeneousObject<FilterCondition> = {
clusters: {
label: 'filter.clusters',
options: [],
@@ -507,18 +507,25 @@ export class LogsContainerService {
query: ['includeQuery', 'excludeQuery']
};
+ readonly topResourcesCount: string = '10';
+
+ readonly topUsersCount: string = '6';
+
readonly logsTypeMap = {
auditLogs: {
logsModel: this.auditLogsStorage,
fieldsModel: this.auditLogsFieldsStorage,
// TODO add all the required fields
listFilters: ['clusters', 'timeRange', 'auditLogsSorting', 'pageSize', 'page', 'query'],
+ topResourcesFilters: ['clusters', 'timeRange', 'query'],
histogramFilters: ['clusters', 'timeRange', 'query']
},
serviceLogs: {
logsModel: this.serviceLogsStorage,
fieldsModel: this.serviceLogsFieldsStorage,
- listFilters: ['clusters', 'timeRange', 'components', 'levels', 'hosts', 'serviceLogsSorting', 'pageSize', 'page', 'query'],
+ listFilters: [
+ 'clusters', 'timeRange', 'components', 'levels', 'hosts', 'serviceLogsSorting', 'pageSize', 'page', 'query'
+ ],
histogramFilters: ['clusters', 'timeRange', 'components', 'levels', 'hosts', 'query']
}
};
@@ -620,6 +627,10 @@ export class LogsContainerService {
private stopCaptureTime: number;
+ topUsersGraphData: HomogeneousObject<HomogeneousObject<number>> = {};
+
+ topResourcesGraphData: HomogeneousObject<HomogeneousObject<number>> = {};
+
loadLogs = (logsType: LogsType = this.activeLogsType): void => {
this.httpClient.get(logsType, this.getParams('listFilters')).subscribe((response: Response): void => {
const jsonResponse = response.json(),
@@ -647,6 +658,34 @@ export class LogsContainerService {
}
});
}
+ if (logsType === 'auditLogs') {
+ this.httpClient.get('topAuditLogsResources', this.getParams('topResourcesFilters', {
+ field: 'resource'
+ }), {
+ number: this.topResourcesCount
+ }).subscribe((response: Response): void => {
+ const jsonResponse = response.json();
+ if (jsonResponse) {
+ const data = jsonResponse.graphData;
+ if (data) {
+ this.topResourcesGraphData = this.parseAuditLogsTopData(data);
+ }
+ }
+ });
+ this.httpClient.get('topAuditLogsResources', this.getParams('topResourcesFilters', {
+ field: 'reqUser'
+ }), {
+ number: this.topUsersCount
+ }).subscribe((response: Response): void => {
+ const jsonResponse = response.json();
+ if (jsonResponse) {
+ const data = jsonResponse.graphData;
+ if (data) {
+ this.topUsersGraphData = this.parseAuditLogsTopData(data);
+ }
+ }
+ });
+ }
};
loadLogContext(id: string, hostName: string, componentName: string, scrollType: ScrollType = ''): void {
@@ -680,7 +719,19 @@ export class LogsContainerService {
});
}
- private getParams(filtersMapName: string, logsType: LogsType = this.activeLogsType): {[key: string]: string} {
+ private parseAuditLogsTopData(data: BarGraph[]): HomogeneousObject<HomogeneousObject<number>> {
+ return data.reduce((currentObject: HomogeneousObject<HomogeneousObject<number>>, currentItem: BarGraph): HomogeneousObject<HomogeneousObject<number>> => Object.assign(currentObject, {
+ [currentItem.name]: currentItem.dataCount.reduce((currentDataObject: HomogeneousObject<number>, currentDataItem: CommonEntry): HomogeneousObject<number> => {
+ return Object.assign(currentDataObject, {
+ [currentDataItem.name]: currentDataItem.value
+ });
+ }, {})
+ }), {});
+ }
+
+ private getParams(
+ filtersMapName: string, additionalParams: HomogeneousObject<string> = {}, logsType: LogsType = this.activeLogsType
+ ): HomogeneousObject<string> {
let params = {};
this.logsTypeMap[logsType][filtersMapName].forEach((key: string): void => {
const inputValue = this.filtersForm.getRawValue()[key],
@@ -698,10 +749,10 @@ export class LogsContainerService {
}
});
}, this);
- return params;
+ return Object.assign({}, params, additionalParams);
}
- getHistogramData(data: BarGraph[]): {[key: string]: number} {
+ getHistogramData(data: BarGraph[]): HomogeneousObject<HomogeneousObject<number>> {
let histogramData = {};
data.forEach(type => {
const name = type.name;
@@ -800,7 +851,9 @@ export class LogsContainerService {
return (value: SearchBoxParameter[]): string => {
let parameters;
if (value && value.length) {
- parameters = value.filter((item: SearchBoxParameter): boolean => item.isExclude === isExclude).map((parameter: SearchBoxParameter): {[key: string]: string} => {
+ parameters = value.filter((item: SearchBoxParameter): boolean => {
+ return item.isExclude === isExclude;
+ }).map((parameter: SearchBoxParameter): HomogeneousObject<string> => {
return {
[parameter.name]: parameter.value.replace(/\s/g, '+')
};