You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2018/01/02 16:56:25 UTC

[34/37] ambari git commit: AMBARI-22714 Log Search UI: implement Summary tab for Access Logs page. (ababiichuk)

AMBARI-22714 Log Search UI: implement Summary tab for Access Logs page. (ababiichuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/aa5b0fe7
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/aa5b0fe7
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/aa5b0fe7

Branch: refs/heads/branch-feature-AMBARI-21674
Commit: aa5b0fe7625249d497a8b50e4fe95ed0583b45b7
Parents: 1c602b0
Author: ababiichuk <ab...@hortonworks.com>
Authored: Tue Jan 2 15:08:44 2018 +0200
Committer: ababiichuk <ab...@hortonworks.com>
Committed: Tue Jan 2 15:43:32 2018 +0200

----------------------------------------------------------------------
 .../ambari-logsearch-web/package.json           |   2 +
 .../ambari-logsearch-web/src/app/app.module.ts  |  15 +
 .../components/graph/graph.component.less       |  48 +++
 .../classes/components/graph/graph.component.ts | 355 +++++++++++++++++++
 .../components/logs-table-component.spec.ts     |  61 ----
 .../classes/components/logs-table-component.ts  |  51 ---
 .../logs-table/logs-table-component.spec.ts     |  61 ++++
 .../logs-table/logs-table-component.ts          |  51 +++
 .../src/app/classes/graph.ts                    |  42 +++
 .../src/app/classes/histogram-options.ts        |  35 --
 .../src/app/classes/models/tab.ts               |   7 +-
 .../src/app/classes/object.ts                   |  19 +
 .../classes/queries/audit-logs-query-params.ts  |  19 +-
 .../audit-logs-top-resources-query-params.ts    |  23 ++
 .../queries/service-logs-query-params.ts        |   4 +-
 .../src/app/classes/service-injector.ts         |  23 ++
 .../audit-logs-entries.component.html           |  30 ++
 .../audit-logs-entries.component.spec.ts        | 110 ++++++
 .../audit-logs-entries.component.ts             |  86 +++++
 .../audit-logs-table.component.ts               |   2 +-
 .../collapsible-panel.component.html            |   2 +-
 .../collapsible-panel.component.ts              |   4 +-
 .../dropdown-button.component.spec.ts           |  10 +-
 .../dropdown-button.component.ts                |   6 +-
 .../filter-button.component.spec.ts             |  10 +-
 .../filter-button/filter-button.component.ts    |   7 +-
 .../filter-dropdown.component.spec.ts           |  14 +-
 .../filter-dropdown.component.ts                |   5 +-
 .../filters-panel/filters-panel.component.ts    |   7 +-
 .../graph-legend-item.component.html            |  19 +
 .../graph-legend-item.component.less            |  27 ++
 .../graph-legend-item.component.spec.ts         |  42 +++
 .../graph-legend-item.component.ts              |  37 ++
 .../graph-legend/graph-legend.component.html    |  19 +
 .../graph-legend/graph-legend.component.spec.ts |  50 +++
 .../graph-legend/graph-legend.component.ts      |  32 ++
 .../graph-tooltip/graph-tooltip.component.html  |  22 ++
 .../graph-tooltip/graph-tooltip.component.less  |  69 ++++
 .../graph-tooltip.component.spec.ts             |  50 +++
 .../graph-tooltip/graph-tooltip.component.ts    |  36 ++
 .../horizontal-histogram.component.html         |  22 ++
 .../horizontal-histogram.component.less         |  22 ++
 .../horizontal-histogram.component.spec.ts      |  61 ++++
 .../horizontal-histogram.component.ts           | 114 ++++++
 .../logs-container.component.html               |   6 +-
 .../logs-container/logs-container.component.ts  |  10 +-
 .../menu-button/menu-button.component.spec.ts   |  10 +-
 .../menu-button/menu-button.component.ts        |   6 +-
 .../search-box/search-box.component.ts          |   3 +-
 .../service-logs-table.component.ts             |   2 +-
 .../app/components/tabs/tabs.component.spec.ts  |   4 -
 .../src/app/components/tabs/tabs.component.ts   |   1 +
 .../time-histogram.component.html               |  32 +-
 .../time-histogram.component.less               | 145 +-------
 .../time-histogram.component.spec.ts            | 104 +++---
 .../time-histogram/time-histogram.component.ts  | 314 +++-------------
 .../components/top-menu/top-menu.component.ts   |   3 +-
 .../src/app/components/variables.less           |   3 +
 .../ambari-logsearch-web/src/app/mock-data.ts   | 142 +++++---
 .../app/services/component-actions.service.ts   |   1 -
 .../src/app/services/http-client.service.ts     |  39 +-
 .../app/services/logs-container.service.spec.ts |   4 +-
 .../src/app/services/logs-container.service.ts  |  85 ++++-
 .../src/app/services/utils.service.spec.ts      | 117 ++++++
 .../src/app/services/utils.service.ts           |  12 +
 .../src/assets/i18n/en.json                     |   6 +-
 .../ambari-logsearch-web/src/styles.less        |   4 +
 .../src/vendor/css/bootstrap-logsearch.min.css  |   2 +-
 .../src/vendor/js/bootstrap-logsearch.min.js    |   2 +-
 ambari-logsearch/ambari-logsearch-web/yarn.lock |  10 +
 70 files changed, 2056 insertions(+), 742 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/package.json
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/package.json b/ambari-logsearch/ambari-logsearch-web/package.json
index b9ee179..2a3df23 100644
--- a/ambari-logsearch/ambari-logsearch-web/package.json
+++ b/ambari-logsearch/ambari-logsearch-web/package.json
@@ -30,6 +30,7 @@
     "bootstrap": "^3.3.7",
     "core-js": "^2.4.1",
     "d3": "^4.10.0",
+    "d3-scale-chromatic": "^1.1.1",
     "font-awesome": "^4.7.0",
     "jquery": "^1.12.4",
     "moment": "^2.18.1",
@@ -43,6 +44,7 @@
     "@angular/compiler-cli": "^4.0.0",
     "@ngtools/webpack": "^1.7.1",
     "@types/d3": "^4.10.0",
+    "@types/d3-scale-chromatic": "^1.1.0",
     "@types/jasmine": "2.5.38",
     "@types/jquery": "^1.10.33",
     "@types/moment": "^2.13.0",

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
index b76de20..da78a71 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
@@ -30,6 +30,8 @@ import {MomentTimezoneModule} from 'angular-moment-timezone';
 
 import {environment} from '@envs/environment';
 
+import {ServiceInjector} from '@app/classes/service-injector';
+
 import {mockApiDataService} from '@app/services/mock-api-data.service'
 import {HttpClientService} from '@app/services/http-client.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
@@ -85,6 +87,11 @@ import {LogFileEntryComponent} from '@app/components/log-file-entry/log-file-ent
 import {TabsComponent} from '@app/components/tabs/tabs.component';
 import {ServiceLogsTableComponent} from '@app/components/service-logs-table/service-logs-table.component';
 import {AuditLogsTableComponent} from '@app/components/audit-logs-table/audit-logs-table.component';
+import {AuditLogsEntriesComponent} from '@app/components/audit-logs-entries/audit-logs-entries.component';
+import {GraphLegendComponent} from '@app/components/graph-legend/graph-legend.component';
+import {HorizontalHistogramComponent} from '@app/components/horizontal-histogram/horizontal-histogram.component';
+import {GraphTooltipComponent} from '@app/components/graph-tooltip/graph-tooltip.component';
+import {GraphLegendItemComponent} from '@app/components/graph-legend-item/graph-legend-item.component';
 
 import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe';
 import {TimerSecondsPipe} from '@app/pipes/timer-seconds.pipe';
@@ -141,6 +148,11 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR
     TabsComponent,
     ServiceLogsTableComponent,
     AuditLogsTableComponent,
+    AuditLogsEntriesComponent,
+    GraphLegendComponent,
+    HorizontalHistogramComponent,
+    GraphTooltipComponent,
+    GraphLegendItemComponent,
     TimeZoneAbbrPipe,
     TimerSecondsPipe
   ],
@@ -194,4 +206,7 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR
   schemas: [CUSTOM_ELEMENTS_SCHEMA]
 })
 export class AppModule {
+  constructor(private injector: Injector) {
+    ServiceInjector.injector = this.injector;
+  }
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less
new file mode 100644
index 0000000..0830193
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.less
@@ -0,0 +1,48 @@
+/**
+ * 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 '../../../components/variables';
+
+:host {
+  display: block;
+
+  /deep/ .axis {
+    .domain {
+      stroke: @base-font-color;
+    }
+    .tick {
+      line {
+        display: none;
+      }
+    }
+  }
+
+  /deep/ .value {
+    cursor: pointer;
+    rect {
+      transition: opacity 250ms;
+      opacity: .8;
+      &:hover {
+        opacity: 1;
+      }
+    }
+  }
+
+  graph-legend {
+    font-size: 1rem;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
new file mode 100644
index 0000000..1bb7f92
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
@@ -0,0 +1,355 @@
+/**
+ * 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 {AfterViewInit, OnChanges, SimpleChanges, ViewChild, ElementRef, Input} from '@angular/core';
+import * as d3 from 'd3';
+import * as d3sc from 'd3-scale-chromatic';
+import {GraphPositionOptions, GraphMarginOptions, GraphTooltipInfo, LegendItem} from '@app/classes/graph';
+import {HomogeneousObject} from '@app/classes/object';
+import {ServiceInjector} from '@app/classes/service-injector';
+import {UtilsService} from '@app/services/utils.service';
+
+export class GraphComponent implements AfterViewInit, OnChanges {
+
+  constructor() {
+    this.utils = ServiceInjector.injector.get(UtilsService);
+  }
+
+  ngAfterViewInit() {
+    this.graphContainer = this.graphContainerRef.nativeElement;
+    this.tooltip = this.tooltipRef.nativeElement;
+    this.host = d3.select(this.graphContainer);
+  }
+
+  ngOnChanges(changes: SimpleChanges) {
+    const dataChange = changes.data;
+    if (dataChange && dataChange.currentValue && !this.utils.isEmptyObject(dataChange.currentValue)
+      && (!dataChange.previousValue || this.utils.isEmptyObject(dataChange.previousValue))
+      && this.utils.isEmptyObject(this.labels)) {
+      this.setDefaultLabels();
+    }
+    this.createGraph();
+  }
+
+  @Input()
+  data: HomogeneousObject<HomogeneousObject<number>> = {};
+
+  @Input()
+  svgId: string = 'graph-svg';
+
+  @Input()
+  margin: GraphMarginOptions = {
+    top: 5,
+    right: 50,
+    bottom: 30,
+    left: 50
+  };
+
+  @Input()
+  width: number;
+
+  @Input()
+  height: number = 150;
+
+  @Input()
+  tickPadding: number = 10;
+
+  @Input()
+  colors: HomogeneousObject<string> = {};
+
+  @Input()
+  labels: HomogeneousObject<string> = {};
+
+  /**
+   * Indicates whether the graph represents dependency on time
+   * @type {boolean}
+   */
+  @Input()
+  isTimeGraph: boolean = false;
+
+  /**
+   * Indicates whether X axis direction is right to left
+   * @type {boolean}
+   */
+  @Input()
+  reverseXRange: boolean = false;
+
+  /**
+   * Indicates whether Y axis direction is top to bottom
+   * @type {boolean}
+   */
+  @Input()
+  reverseYRange: boolean = false;
+
+  @ViewChild('graphContainer')
+  graphContainerRef: ElementRef;
+
+  @ViewChild('tooltip', {
+    read: ElementRef
+  })
+  tooltipRef: ElementRef;
+
+  protected utils: UtilsService;
+
+  protected graphContainer: HTMLElement;
+
+  private tooltip: HTMLElement;
+
+  protected host;
+
+  protected svg;
+
+  protected xScale;
+
+  protected yScale;
+
+  protected xAxis;
+
+  protected yAxis;
+
+  /**
+   * Ordered array of color strings for data representation
+   * @type {string[]}
+   */
+  protected orderedColors: string[];
+
+  /**
+   * This property is to hold the data of the bar where the mouse is over.
+   */
+  protected tooltipInfo: GraphTooltipInfo | {} = {};
+
+  /**
+   * This is the computed position of the tooltip relative to the @graphContainer which is the container of the histogram.
+   * It is set when the mousemoving over the bars in the @handleRectMouseMove method.
+   */
+  private tooltipPosition: GraphPositionOptions;
+
+  /**
+   * 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 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 {LegendItem[]}
+   */
+  get legendItems(): LegendItem[] {
+    return Object.keys(this.labels).map((key: string) => Object.assign({}, {
+      label: this.labels[key],
+      color: this.colors[key]
+    }));
+  }
+
+  protected createGraph(): void {
+    if (this.host && !this.utils.isEmptyObject(this.labels)) {
+      this.setup();
+      this.buildSVG();
+      this.populate();
+    }
+  }
+
+  /**
+   * Method that sets default labels map object based on data if no custom one is specified
+   */
+  protected setDefaultLabels() {
+    const data = this.data,
+      keys = Object.keys(data),
+      labels = keys.reduce((keys: HomogeneousObject<string>, dataKey: string): HomogeneousObject<string> => {
+        const newKeys = Object.keys(data[dataKey]),
+          newKeysObj = newKeys.reduce((subKeys: HomogeneousObject<string>, key: string): HomogeneousObject<string> => {
+            return Object.assign(subKeys, {
+              [key]: key
+            });
+        }, {});
+        return Object.assign(keys, newKeysObj);
+      }, {});
+    this.labels = labels;
+  }
+
+  protected setup(): void {
+    const margin = this.margin;
+    if (this.utils.isEmptyObject(this.colors)) {
+      // set default color scheme for different values if no custom colors specified
+      const keys = Object.keys(this.labels),
+        keysCount = keys.length,
+        specterLength = keysCount > 2 ? keysCount : 3; // length of minimal available spectral scheme is 3
+      let colorsArray;
+      if (keysCount > 2) {
+        colorsArray = Array.from(d3sc.schemeSpectral[keysCount]);
+      } else {
+        const minimalColorScheme = Array.from(d3sc.schemeSpectral[specterLength]);
+        colorsArray = minimalColorScheme.slice(0, keysCount);
+      }
+      this.orderedColors = colorsArray;
+      this.colors = keys.reduce((currentObject: HomogeneousObject<string>, currentKey: string, index: number) => {
+        return Object.assign(currentObject, {
+          [currentKey]: colorsArray[index]
+        });
+      }, {});
+    } else {
+      const keysWithColors = this.colors,
+        keys = Object.keys(keysWithColors);
+      this.orderedColors = keys.reduce((array: string[], key: string): string[] => [...array, keysWithColors[key]], []);
+    }
+    if (!this.width) {
+      this.width = this.graphContainer.clientWidth - margin.left - margin.right;
+    }
+    const xScale = this.isTimeGraph ? d3.scaleTime() : d3.scaleLinear();
+    const yScale = d3.scaleLinear();
+    const xScaleWithRange = this.reverseXRange ? xScale.range([this.width, 0]) : xScale.range([0, this.width]);
+    const yScaleWithRange = this.reverseYRange ? yScale.range([0, this.height]) : yScale.range([this.height, 0]);
+    this.xScale = xScaleWithRange;
+    this.yScale = yScaleWithRange;
+  }
+
+  protected buildSVG(): void {
+    const margin = this.margin;
+    this.host.html('');
+    this.svg = this.host.append('svg').attr('id', this.svgId).attr('width', this.graphContainer.clientWidth)
+      .attr('height', this.height + margin.top + margin.bottom).append('g')
+      .attr('transform', `translate(${margin.left},${margin.top})`);
+  }
+
+  protected populate(): void {}
+
+  protected setXScaleDomain(formattedData?: any): void {}
+
+  protected setYScaleDomain(formattedData?: any): void {}
+
+  /**
+   * 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.
+   * @param {number} ticksCount - optional parameter which sets number of ticks explicitly
+   */
+  protected drawXAxis(ticksCount?: number): void {
+    const axis = d3.axisBottom(this.xScale).tickFormat(this.xAxisTickFormatter).tickPadding(this.tickPadding);
+    if (ticksCount) {
+      axis.ticks(ticksCount);
+    }
+    this.xAxis = axis;
+    this.svg.append('g').attr('class', 'axis axis-x').attr('transform', `translate(0,${this.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.
+   * @param {number} ticksCount - optional parameter which sets number of ticks explicitly
+   */
+  protected drawYAxis(ticksCount?: number): void {
+    const axis = d3.axisLeft(this.yScale).tickFormat(this.yAxisTickFormatter).tickPadding(this.tickPadding);
+    if (ticksCount) {
+      axis.ticks(ticksCount);
+    }
+    this.yAxis = axis;
+    this.svg.append('g').attr('class', 'axis axis-y').call(this.yAxis).append('text');
+  };
+
+  /**
+   * Function that formats the labels for X axis ticks.
+   * Returns simple toString() conversion as default, can be overridden in ancestors.
+   * undefined value is returned for ticks to be skipped.
+   * @param tick
+   * @param {number} index
+   * @returns {string|undefined}
+   */
+  protected xAxisTickFormatter = (tick: any, index: number): string | undefined => {
+    return tick.toString();
+  };
+
+  /**
+   * Function that formats the labels for Y axis ticks.
+   * Returns simple toString() conversion as default, can be overridden in ancestors.
+   * undefined value is returned for ticks to be skipped.
+   * @param tick
+   * @param {number} index
+   * @returns {string|undefined}
+   */
+  protected yAxisTickFormatter = (tick: any, index: number): string | undefined => {
+    return tick.toString();
+  };
+
+  /**
+   * 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
+   */
+  protected handleRectMouseOver = (d: {data: any, [key: string]: 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.
+   */
+  protected handleRectMouseMove = (): void => {
+    this.setTooltipPosition();
+  };
+
+  /**
+   * The goal is to reset the tooltipInfo object so that the tooltip will be hidden.
+   */
+  protected handleRectMouseOut = (): void => {
+    this.tooltipInfo = {};
+  };
+
+  /**
+   * The goal is set the tooltip
+   * @param d
+   */
+  protected setTooltipDataFromChartData(d: {data: any, [key: string]: any}): void {
+    let {tick, ...data} = d.data;
+    let levelColors = this.colors;
+    this.tooltipInfo = {
+      data: Object.keys(levelColors).filter((key: string): boolean => data[key] > 0).map((key: string): object => Object.assign({}, {
+        color: this.colors[key],
+        label: this.labels[key],
+        value: data[key]
+      })),
+      title: tick
+    };
+  }
+
+  /**
+   * The goal of this function is to set the tooltip position regarding the d3.mouse event relative to the @graphContainer.
+   * Only if we have @tooltipInfo
+   */
+  protected setTooltipPosition(): void {
+    if (this.tooltipInfo.hasOwnProperty('data')) {
+      const tooltip = this.tooltip,
+        relativeMousePosition = d3.mouse(this.graphContainer),
+        absoluteMousePosition = d3.mouse(document.body),
+        absoluteMouseLeft = absoluteMousePosition[0],
+        top = relativeMousePosition[1] - (tooltip.offsetHeight / 2),
+        tooltipWidth = tooltip.offsetWidth,
+        windowSize = window.innerWidth;
+      let left = relativeMousePosition[0];
+      if (absoluteMouseLeft + tooltipWidth > windowSize) {
+        left = relativeMousePosition[0] - (tooltipWidth + 25);
+      }
+      this.tooltipOnTheLeft = left < relativeMousePosition[0];
+      this.tooltipPosition = {left, top};
+    }
+  };
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.spec.ts
deleted file mode 100644
index 05f80a7..0000000
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.spec.ts
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * 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 {LogsTableComponent} from './logs-table-component';
-
-describe('LogsTableComponent', () => {
-  let component;
-
-  beforeEach(() => {
-    component = new LogsTableComponent();
-  });
-
-  describe('#isColumnDisplayed()', () => {
-    const cases = [
-      {
-        name: 'v1',
-        result: true,
-        title: 'column is displayed'
-      },
-      {
-        name: 'l1',
-        result: false,
-        title: 'column is not displayed'
-      }
-    ];
-
-    beforeEach(() => {
-      component.displayedColumns = [
-        {
-          label: 'l0',
-          value: 'v0'
-        },
-        {
-          label: 'l1',
-          value: 'v1'
-        }
-      ];
-    });
-
-    cases.forEach(test => {
-      it(test.title, () => {
-        expect(component.isColumnDisplayed(test.name)).toEqual(test.result);
-      });
-    });
-  });
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.ts
deleted file mode 100644
index 0b8866a..0000000
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table-component.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- * 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 {OnChanges, SimpleChanges, Input} from '@angular/core';
-import {FormGroup} from '@angular/forms';
-import {ListItem} from '@app/classes/list-item';
-import {ServiceLog} from '@app/classes/models/service-log';
-import {AuditLog} from '@app/classes/models/audit-log';
-
-export class LogsTableComponent implements OnChanges {
-
-  ngOnChanges(changes: SimpleChanges) {
-    if (changes.hasOwnProperty('columns')) {
-      this.displayedColumns = this.columns.filter((column: ListItem): boolean => column.isChecked);
-    }
-  }
-
-  @Input()
-  logs: ServiceLog[] | AuditLog[] = [];
-
-  @Input()
-  columns: ListItem[] = [];
-
-  @Input()
-  filtersForm: FormGroup;
-
-  @Input()
-  totalCount: number = 0;
-
-  displayedColumns: ListItem[] = [];
-
-  isColumnDisplayed(key: string): boolean {
-    return this.displayedColumns.some((column: ListItem): boolean => column.value === key);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-component.spec.ts
new file mode 100644
index 0000000..05f80a7
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-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 {LogsTableComponent} from './logs-table-component';
+
+describe('LogsTableComponent', () => {
+  let component;
+
+  beforeEach(() => {
+    component = new LogsTableComponent();
+  });
+
+  describe('#isColumnDisplayed()', () => {
+    const cases = [
+      {
+        name: 'v1',
+        result: true,
+        title: 'column is displayed'
+      },
+      {
+        name: 'l1',
+        result: false,
+        title: 'column is not displayed'
+      }
+    ];
+
+    beforeEach(() => {
+      component.displayedColumns = [
+        {
+          label: 'l0',
+          value: 'v0'
+        },
+        {
+          label: 'l1',
+          value: 'v1'
+        }
+      ];
+    });
+
+    cases.forEach(test => {
+      it(test.title, () => {
+        expect(component.isColumnDisplayed(test.name)).toEqual(test.result);
+      });
+    });
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-component.ts
new file mode 100644
index 0000000..0b8866a
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/logs-table/logs-table-component.ts
@@ -0,0 +1,51 @@
+/**
+ * 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 {OnChanges, SimpleChanges, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+import {ListItem} from '@app/classes/list-item';
+import {ServiceLog} from '@app/classes/models/service-log';
+import {AuditLog} from '@app/classes/models/audit-log';
+
+export class LogsTableComponent implements OnChanges {
+
+  ngOnChanges(changes: SimpleChanges) {
+    if (changes.hasOwnProperty('columns')) {
+      this.displayedColumns = this.columns.filter((column: ListItem): boolean => column.isChecked);
+    }
+  }
+
+  @Input()
+  logs: ServiceLog[] | AuditLog[] = [];
+
+  @Input()
+  columns: ListItem[] = [];
+
+  @Input()
+  filtersForm: FormGroup;
+
+  @Input()
+  totalCount: number = 0;
+
+  displayedColumns: ListItem[] = [];
+
+  isColumnDisplayed(key: string): boolean {
+    return this.displayedColumns.some((column: ListItem): boolean => column.value === key);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts
new file mode 100644
index 0000000..3992828
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/graph.ts
@@ -0,0 +1,42 @@
+/**
+ * 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 interface GraphPositionOptions {
+  top: number;
+  left: number;
+}
+
+export interface GraphMarginOptions extends GraphPositionOptions {
+  right: number;
+  bottom: number;
+}
+
+export interface GraphTooltipInfo {
+  data: object[];
+  title: string | number;
+}
+
+export interface LegendItem {
+  label: string;
+  color: string;
+}
+
+export interface GraphScaleItem {
+  tick: number;
+  [key: string]: number;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/histogram-options.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/histogram-options.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/histogram-options.ts
deleted file mode 100644
index 15fefde..0000000
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/histogram-options.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * 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 interface HistogramMarginOptions {
-  top: number;
-  right: number;
-  bottom: number;
-  left: number;
-}
-
-export interface HistogramStyleOptions {
-  margin?: HistogramMarginOptions;
-  height?: number;
-  tickPadding?: number;
-  columnWidth?: {[key:string]: number};
-}
-
-export interface HistogramOptions extends HistogramStyleOptions {
-  keysWithColors: {[key: string]: string};
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/tab.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/tab.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/tab.ts
index 05ea59d..718adf3 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/tab.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/tab.ts
@@ -16,19 +16,19 @@
  * limitations under the License.
  */
 
+import {HomogeneousObject} from '@app/classes/object';
+
 export interface Tab {
   id: string;
-  type: string;
   isActive?: boolean;
   isCloseable?: boolean;
   label: string;
-  appState?: object;
+  appState?: HomogeneousObject<any>;
 }
 
 export const initialTabs: Tab[] = [
   {
     id: 'serviceLogs',
-    type: 'serviceLogs',
     isActive: true,
     label: 'common.serviceLogs',
     appState: {
@@ -38,7 +38,6 @@ export const initialTabs: Tab[] = [
   },
   {
     id: 'auditLogs',
-    type: 'auditLogs',
     isActive: false,
     label: 'common.auditLogs',
     appState: {

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts
new file mode 100644
index 0000000..4d0c7f6
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/object.ts
@@ -0,0 +1,19 @@
+/**
+ * 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 HomogeneousObject<T> = {[key: string]: T};

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 3b38a03..dc82b9e 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
@@ -25,7 +25,17 @@ export const defaultParams = {
 };
 
 export class AuditLogsQueryParams extends QueryParams {
-  constructor(options: AuditLogsQueryParams) {
+  clusters?: string;
+  mustBe?: string;
+  mustNot?: string;
+  includeQuery?: string;
+  excludeQuery?: string;
+  from?: string;
+  to?: string;
+}
+
+export class AuditLogsListQueryParams extends AuditLogsQueryParams {
+  constructor(options: AuditLogsListQueryParams) {
     let finalParams = Object.assign({}, defaultParams, options);
     const page = parseInt(finalParams.page),
       pageSize = parseInt(finalParams.pageSize);
@@ -37,11 +47,4 @@ export class AuditLogsQueryParams extends QueryParams {
   startIndex: string;
   sortBy?: string;
   sortType?: SortingType;
-  clusters?: string;
-  mustBe?: string;
-  mustNot?: string;
-  includeQuery?: string;
-  excludeQuery?: string;
-  from?: string;
-  to?: string;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts
new file mode 100644
index 0000000..0d12539
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-top-resources-query-params.ts
@@ -0,0 +1,23 @@
+/**
+ * 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 {AuditLogsQueryParams} from '@app/classes/queries/audit-logs-query-params';
+
+export class AuditLogsTopResourcesQueryParams extends AuditLogsQueryParams {
+  field: string;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts
index 0700a98..60c3d5c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/service-logs-query-params.ts
@@ -16,9 +16,9 @@
  * limitations under the License.
  */
 
-import {AuditLogsQueryParams} from '@app/classes/queries/audit-logs-query-params';
+import {AuditLogsListQueryParams} from '@app/classes/queries/audit-logs-query-params';
 
-export class ServiceLogsQueryParams extends AuditLogsQueryParams {
+export class ServiceLogsQueryParams extends AuditLogsListQueryParams {
   level?: string;
   file_name?: string;
   bundle_id?: string;

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-injector.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-injector.ts b/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-injector.ts
new file mode 100644
index 0000000..6db65cd
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/service-injector.ts
@@ -0,0 +1,23 @@
+/**
+ * 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';
+
+export class ServiceInjector {
+  static injector: Injector;
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
new file mode 100644
index 0000000..3c5852a
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
@@ -0,0 +1,30 @@
+<!--
+  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.
+-->
+
+<tabs [items]="tabs" (tabSwitched)="setActiveTab($event)"></tabs>
+<ng-container [ngSwitch]="activeTab">
+  <audit-logs-table *ngSwitchCase="'logs'" [totalCount]="totalCount" [logs]="logs" [columns]="columns"
+                    [filtersForm]="filtersForm"></audit-logs-table>
+  <div *ngSwitchCase="'summary'" class="row">
+    <collapsible-panel title="{{'logs.topUsers' | translate: usersGraphTitleParams}}" class="col-md-6">
+      <horizontal-histogram [data]="topUsersGraphData"></horizontal-histogram>
+    </collapsible-panel>
+    <collapsible-panel title="{{'logs.topResources' | translate: resourcesGraphTitleParams}}" class="col-md-6">
+      <horizontal-histogram [data]="topResourcesGraphData"></horizontal-histogram>
+    </collapsible-panel>
+  </div>
+</ng-container>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
new file mode 100644
index 0000000..260b383
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
@@ -0,0 +1,110 @@
+/**
+ * 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 {StoreModule} from '@ngrx/store';
+import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
+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 {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';
+import {ComponentsService, components} from '@app/services/storage/components.service';
+import {HostsService, hosts} from '@app/services/storage/hosts.service';
+import {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
+import {TabsService, tabs} from '@app/services/storage/tabs.service';
+import {TabsComponent} from '@app/components/tabs/tabs.component';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {HttpClientService} from '@app/services/http-client.service';
+
+import {AuditLogsEntriesComponent} from './audit-logs-entries.component';
+
+describe('AuditLogsEntriesComponent', () => {
+  let component: AuditLogsEntriesComponent;
+  let fixture: ComponentFixture<AuditLogsEntriesComponent>;
+
+  beforeEach(async(() => {
+    const httpClient = {
+      get: () => {
+        return {
+          subscribe: () => {
+          }
+        }
+      }
+    };
+    TestBed.configureTestingModule({
+      declarations: [
+        AuditLogsEntriesComponent,
+        TabsComponent
+      ],
+      imports: [
+        ...TranslationModules,
+        StoreModule.provideStore({
+          auditLogs,
+          serviceLogs,
+          auditLogsFields,
+          serviceLogsFields,
+          serviceLogsHistogramData,
+          appSettings,
+          appState,
+          clusters,
+          components,
+          hosts,
+          serviceLogsTruncated,
+          tabs
+        }),
+      ],
+      providers: [
+        LogsContainerService,
+        {
+          provide: HttpClientService,
+          useValue: httpClient
+        },
+        AuditLogsService,
+        ServiceLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
+        AppSettingsService,
+        AppStateService,
+        ClustersService,
+        ComponentsService,
+        HostsService,
+        ServiceLogsTruncatedService,
+        TabsService
+      ],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AuditLogsEntriesComponent);
+    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/audit-logs-entries/audit-logs-entries.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.ts
new file mode 100644
index 0000000..44786f1
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.ts
@@ -0,0 +1,86 @@
+/**
+ * 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 {FormGroup} from '@angular/forms';
+import {ListItem} from '@app/classes/list-item';
+import {HomogeneousObject} from '@app/classes/object';
+import {AuditLog} from '@app/classes/models/audit-log';
+import {Tab} from '@app/classes/models/tab';
+import {LogsContainerService} from '@app/services/logs-container.service';
+
+@Component({
+  selector: 'audit-logs-entries',
+  templateUrl: './audit-logs-entries.component.html'
+})
+export class AuditLogsEntriesComponent {
+
+  constructor(private logsContainer: LogsContainerService) {
+  }
+
+  @Input()
+  logs: AuditLog[] = [];
+
+  @Input()
+  columns: ListItem[] = [];
+
+  @Input()
+  filtersForm: FormGroup;
+
+  @Input()
+  totalCount: number = 0;
+
+  tabs: Tab[] = [
+    {
+      id: 'summary',
+      isActive: true,
+      label: 'common.summary'
+    },
+    {
+      id: 'logs',
+      isActive: false,
+      label: 'common.logs'
+    }
+  ];
+
+  /**
+   * Id of currently active tab (Summary or Logs)
+   * @type {string}
+   */
+  activeTab: string = 'summary';
+
+  readonly usersGraphTitleParams = {
+    number: this.logsContainer.topUsersCount
+  };
+
+  readonly resourcesGraphTitleParams = {
+    number: this.logsContainer.topResourcesCount
+  };
+
+  get topResourcesGraphData(): HomogeneousObject<HomogeneousObject<number>> {
+    return this.logsContainer.topResourcesGraphData;
+  }
+
+  get topUsersGraphData(): HomogeneousObject<HomogeneousObject<number>> {
+    return this.logsContainer.topUsersGraphData;
+  }
+
+  setActiveTab(tab: Tab): void {
+    this.activeTab = tab.id;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
index 0e578ab..deca936 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
@@ -18,7 +18,7 @@
 
 import {Component} 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';
 
 @Component({

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
index b73fa45..4d0bd39 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.html
@@ -22,6 +22,6 @@
       </a>
     </div>
     <div [ngClass]="{'panel-body': true}" [attr.aria-collapsed]="isCollapsed">
-        <ng-content></ng-content>
+      <ng-content></ng-content>
     </div>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts
index cbc0dd7..f82823a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/collapsible-panel/collapsible-panel.component.ts
@@ -46,14 +46,14 @@ export class CollapsiblePanelComponent {
    * @type {string}
    */
   @Input()
-  openTitle: string = 'common.hide';
+  openTitle?: string;
 
   /**
    * The panel's title fo the closed/collapsed state
    * @type {string}
    */
   @Input()
-  collapsedTitle: string = 'common.show';
+  collapsedTitle?: string;
 
   /**
    * This property indicates the position of the caret. It can be 'left' or 'right'

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
index fc42e3c..9f2bb16 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
@@ -16,10 +16,11 @@
  * 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 {StoreModule} from '@ngrx/store';
+import {ServiceInjector} from '@app/classes/service-injector';
 import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
 import {ClustersService, clusters} from '@app/services/storage/clusters.service';
 import {ComponentsService, components} from '@app/services/storage/components.service';
@@ -99,11 +100,12 @@ describe('DropdownButtonComponent', () => {
     .compileComponents();
   }));
 
-  beforeEach(() => {
+  beforeEach(inject([Injector], (injector: Injector) => {
+    ServiceInjector.injector = injector;
     fixture = TestBed.createComponent(DropdownButtonComponent);
     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/dropdown-button/dropdown-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
index a8037d0..ead9e1a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
@@ -18,6 +18,7 @@
 
 import {Component, Input} 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';
 import {UtilsService} from '@app/services/utils.service';
 
@@ -28,7 +29,8 @@ import {UtilsService} from '@app/services/utils.service';
 })
 export class DropdownButtonComponent {
 
-  constructor(protected actions: ComponentActionsService, protected utils: UtilsService) {
+  constructor(protected utils: UtilsService) {
+    this.actions = ServiceInjector.injector.get(ComponentActionsService);
   }
   
   @Input()
@@ -64,6 +66,8 @@ export class DropdownButtonComponent {
   @Input()
   isDropup: boolean = false;
 
+  private actions: ComponentActionsService;
+
   protected selectedItems?: ListItem[] = [];
 
   get selection(): ListItem[] {

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
index 6a9aca5..082082c 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
@@ -16,10 +16,11 @@
  * 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 {StoreModule} from '@ngrx/store';
+import {ServiceInjector} from '@app/classes/service-injector';
 import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
 import {ClustersService, clusters} from '@app/services/storage/clusters.service';
 import {ComponentsService, components} from '@app/services/storage/components.service';
@@ -99,11 +100,12 @@ describe('FilterButtonComponent', () => {
     .compileComponents();
   }));
 
-  beforeEach(() => {
+  beforeEach(inject([Injector], (injector: Injector) => {
+    ServiceInjector.injector = injector;
     fixture = TestBed.createComponent(FilterButtonComponent);
     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/filter-button/filter-button.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts
index 2dcecd1..e1787a2 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.ts
@@ -16,10 +16,9 @@
  * limitations under the License.
  */
 
-import {Component, Input, forwardRef} from '@angular/core';
+import {Component, forwardRef} from '@angular/core';
 import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
 import {ListItem} from "@app/classes/list-item";
-import {ComponentActionsService} from '@app/services/component-actions.service';
 import {UtilsService} from '@app/services/utils.service';
 import {MenuButtonComponent} from '@app/components/menu-button/menu-button.component';
 
@@ -37,8 +36,8 @@ import {MenuButtonComponent} from '@app/components/menu-button/menu-button.compo
 })
 export class FilterButtonComponent extends MenuButtonComponent implements ControlValueAccessor {
 
-  constructor(protected actions: ComponentActionsService, private utils: UtilsService) {
-    super(actions);
+  constructor(private utils: UtilsService) {
+    super();
   }
 
   private selectedItems: ListItem[] = [];

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
index 8293ba0..d085f3e 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
@@ -15,17 +15,20 @@
  * 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 {StoreModule} from '@ngrx/store';
+import {ServiceInjector} from '@app/classes/service-injector';
 import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
 import {AppStateService, appState} from '@app/services/storage/app-state.service';
 import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
 import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
 import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.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 {ServiceLogsTruncatedService, serviceLogsTruncated} from '@app/services/storage/service-logs-truncated.service';
 import {TabsService, tabs} from '@app/services/storage/tabs.service';
 import {ClustersService, clusters} from '@app/services/storage/clusters.service';
@@ -118,11 +121,12 @@ describe('FilterDropdownComponent', () => {
     .compileComponents();
   }));
 
-  beforeEach(() => {
+  beforeEach(inject([Injector], (injector: Injector) => {
+    ServiceInjector.injector = injector;
     fixture = TestBed.createComponent(FilterDropdownComponent);
     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/filter-dropdown/filter-dropdown.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts
index d677d81..665386b 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts
@@ -17,7 +17,6 @@
 
 import {Component, forwardRef} from '@angular/core';
 import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
-import {ComponentActionsService} from '@app/services/component-actions.service';
 import {UtilsService} from '@app/services/utils.service';
 import {DropdownButtonComponent} from '@app/components/dropdown-button/dropdown-button.component';
 import {ListItem} from '@app/classes/list-item';
@@ -36,8 +35,8 @@ import {ListItem} from '@app/classes/list-item';
 })
 export class FilterDropdownComponent extends DropdownButtonComponent implements ControlValueAccessor {
 
-  constructor(protected actions: ComponentActionsService, protected utils: UtilsService) {
-    super(actions, utils);
+  constructor(protected utils: UtilsService) {
+    super(utils);
   }
 
   private onChange: (fn: any) => void;

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/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 480706a..cd372ec 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
@@ -23,6 +23,7 @@ import {Subject} from 'rxjs/Subject';
 import 'rxjs/add/observable/from';
 import {FilterCondition, SearchBoxParameter, SearchBoxParameterTriggered} from '@app/classes/filtering';
 import {ListItem} from '@app/classes/list-item';
+import {HomogeneousObject} from '@app/classes/object';
 import {LogsType} from '@app/classes/string';
 import {LogsContainerService} from '@app/services/logs-container.service';
 
@@ -66,15 +67,15 @@ export class FiltersPanelComponent implements OnChanges {
     return this.viewContainerRef.element.nativeElement;
   }
 
-  get filters(): {[key: string]: FilterCondition} {
+  get filters(): HomogeneousObject<FilterCondition> {
     return this.logsContainer.filters;
   }
 
   /**
    * Object with options for search box parameter values
-   * @returns {[key: string]: ListItem[]}
+   * @returns HomogeneousObject<ListItem[]>
    */
-  get options(): {[key: string]: ListItem[]} {
+  get options(): HomogeneousObject<ListItem[]> {
     return Object.keys(this.filters).filter((key: string): boolean => {
       const condition = this.filters[key];
       return Boolean(condition.fieldName && condition.options);

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.html
new file mode 100644
index 0000000..e1ebb70
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.html
@@ -0,0 +1,19 @@
+<!--
+  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.
+-->
+
+<span class="color" [style.background-color]="color"></span>
+{{label}}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.less
new file mode 100644
index 0000000..dc20dca
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.less
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+:host {
+  padding-right: 1em;
+
+  .color {
+    border-radius: 100%;
+    display: inline-block;
+    height: .8em;
+    width: .8em;
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.spec.ts
new file mode 100644
index 0000000..f8a4beb
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.spec.ts
@@ -0,0 +1,42 @@
+/**
+ * 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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {GraphLegendItemComponent} from './graph-legend-item.component';
+
+describe('GraphLegendItemComponent', () => {
+  let component: GraphLegendItemComponent;
+  let fixture: ComponentFixture<GraphLegendItemComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [GraphLegendItemComponent]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(GraphLegendItemComponent);
+    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-legend-item/graph-legend-item.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.ts
new file mode 100644
index 0000000..127eb8d
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend-item/graph-legend-item.component.ts
@@ -0,0 +1,37 @@
+/**
+ * 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-legend-item',
+  templateUrl: './graph-legend-item.component.html',
+  styleUrls: ['./graph-legend-item.component.less']
+})
+export class GraphLegendItemComponent {
+
+  /**
+   * Color of the corresponding graph item. Should be string in any CSS allowable format.
+   * @type {string}
+   */
+  @Input()
+  color: string;
+
+  @Input()
+  label: string;
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.html
new file mode 100644
index 0000000..e756af6
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.html
@@ -0,0 +1,19 @@
+<!--
+  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.
+-->
+
+<graph-legend-item *ngFor="let item of items" label="{{item.label | translate}}"
+                   color="{{item.color}}"></graph-legend-item>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.spec.ts
new file mode 100644
index 0000000..e297e14
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.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 {GraphLegendComponent} from './graph-legend.component';
+
+describe('GraphLegendComponent', () => {
+  let component: GraphLegendComponent;
+  let fixture: ComponentFixture<GraphLegendComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [
+        GraphLegendComponent,
+        GraphLegendItemComponent
+      ],
+      imports: TranslationModules,
+      schemas: [CUSTOM_ELEMENTS_SCHEMA]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(GraphLegendComponent);
+    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-legend/graph-legend.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.ts
new file mode 100644
index 0000000..e273d4e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-legend/graph-legend.component.ts
@@ -0,0 +1,32 @@
+/**
+ * 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-legend',
+  templateUrl: './graph-legend.component.html'
+})
+export class GraphLegendComponent {
+
+  @Input()
+  items = [];
+
+  @Input()
+  labelClass: string = 'initial-color';
+
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.html
new file mode 100644
index 0000000..1711ffc
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.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 class="title">{{title}}</div>
+<div *ngFor="let item of data" class="data-item">
+  <graph-legend-item label="{{item.label | translate}}" color="{{item.color}}"></graph-legend-item>
+  <span class="item-value">{{item.value}}</span>
+</div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/aa5b0fe7/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.less
new file mode 100644
index 0000000..ec750e1
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/graph-tooltip/graph-tooltip.component.less
@@ -0,0 +1,69 @@
+/**
+ * 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 '../mixins';
+
+:host {
+  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;
+    }
+
+    &::after {
+      .caret-mixin(6px, right, #fff);
+      right: -6px;
+      position: absolute;
+      top: calc(50% - 2px);
+    }
+  }
+
+  .title {
+    padding: 0 0 .1em 0;
+    text-align: center;
+  }
+
+  .data-item {
+    display: flex;
+    justify-content: space-between;
+
+    graph-legend-item {
+      flex-grow: 3;
+    }
+  }
+}