You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by mr...@apache.org on 2017/09/11 04:39:07 UTC
[37/94] [abbrv] [partial] ambari git commit: AMBARI-21870. Integrate
LogSearch new UI with the server and get rid of the old one (oleewere)
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/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
new file mode 100644
index 0000000..3e2a6c7
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
@@ -0,0 +1,105 @@
+/**
+ * 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, OnInit, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+import {Observable} from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+import {FilteringService} from '@app/services/filtering.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {ServiceLogsHistogramDataService} from '@app/services/storage/service-logs-histogram-data.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
+import {AuditLog} from '@app/models/audit-log.model';
+import {ServiceLog} from '@app/models/service-log.model';
+import {LogField} from '@app/models/log-field.model';
+
+@Component({
+ selector: 'logs-container',
+ templateUrl: './logs-container.component.html',
+ styleUrls: ['./logs-container.component.less']
+})
+export class LogsContainerComponent implements OnInit {
+
+ constructor(private serviceLogsHistogramStorage: ServiceLogsHistogramDataService, private appState: AppStateService, private filtering: FilteringService, private logsContainer: LogsContainerService) {
+ serviceLogsHistogramStorage.getAll().subscribe(data => this.histogramData = this.logsContainer.getHistogramData(data));
+ }
+
+ ngOnInit() {
+ const fieldsModel = this.logsTypeMapObject.fieldsModel,
+ logsModel = this.logsTypeMapObject.logsModel;
+ this.appState.getParameter(this.logsTypeMapObject.isSetFlag).subscribe(value => this.isLogsSet = value);
+ this.availableColumns = fieldsModel.getAll().map(fields => {
+ return fields.filter(field => field.isAvailable).map(field => {
+ return {
+ value: field.name,
+ label: field.displayName || field.name,
+ isChecked: field.isDisplayed
+ };
+ });
+ });
+ fieldsModel.getAll().subscribe(columns => {
+ const availableFields = columns.filter(field => field.isAvailable),
+ availableNames = availableFields.map(field => field.name);
+ if (availableNames.length && !this.isLogsSet) {
+ this.logs = logsModel.getAll().map(logs => logs.map(log => {
+ let logObject = availableNames.reduce((obj, key) => Object.assign(obj, {
+ [key]: log[key]
+ }), {});
+ if (logObject.level) {
+ logObject.className = logObject.level.toLowerCase();
+ }
+ return logObject;
+ }));
+ this.appState.setParameter(this.logsTypeMapObject.isSetFlag, true);
+ }
+ this.displayedColumns = columns.filter(column => column.isAvailable && column.isDisplayed);
+ });
+ this.logsContainer.loadLogs(this.logsType);
+ this.filtersForm.valueChanges.subscribe(() => this.logsContainer.loadLogs(this.logsType));
+ }
+
+ @Input()
+ logsType: string;
+
+ private isLogsSet: boolean = false;
+
+ get logsTypeMapObject(): any {
+ return this.logsContainer.logsTypeMap[this.logsType];
+ }
+
+ get totalCount(): number {
+ return this.logsContainer.totalCount;
+ }
+
+ logs: Observable<AuditLog[] | ServiceLog[]>;
+
+ availableColumns: Observable<LogField[]>;
+
+ displayedColumns: any[] = [];
+
+ histogramData: any;
+
+ readonly histogramOptions = {
+ keysWithColors: this.logsContainer.colors
+ };
+
+ private get filtersForm(): FormGroup {
+ return this.filtering.filtersForm;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
new file mode 100644
index 0000000..888c524
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.html
@@ -0,0 +1,54 @@
+<!--
+ 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.
+-->
+
+<form *ngIf="logs && logs.length" [formGroup]="filtersForm" class="pull-right">
+ <filter-dropdown [label]="filters.sorting.label" formControlName="sorting" [options]="filters.sorting.options"
+ [defaultLabel]="filters.sorting.defaultLabel" [isRightAlign]="true"></filter-dropdown>
+</form>
+<div class="col-md-12 text-center" *ngIf="logs && logs.length">
+ <div class="logs-header">
+ <div class="col-md-1">{{'logs.status' | translate}}</div>
+ <div class="col-md-11">{{'logs.details' | translate}}</div>
+ </div>
+</div>
+<accordion-panel *ngFor="let log of logs; let i = index" [toggleId]="'details-' + i" class="col-md-12">
+ <ng-template>
+ <div *ngIf="isColumnDisplayed('level')" [ngClass]="'hexagon ' + log.className"></div>
+ <div *ngIf="isColumnDisplayed('level')" [ngClass]="'col-md-1 log-status ' + log.className">{{log.level}}</div>
+ <div *ngIf="isColumnDisplayed('type') || isColumnDisplayed('logtime')" class="col-md-3">
+ <div *ngIf="isColumnDisplayed('type')" class="log-type">{{log.type}}</div>
+ <time *ngIf="isColumnDisplayed('logtime')" class="log-time">
+ {{log.logtime | amTz: timeZone | amDateFormat: timeFormat}}
+ </time>
+ </div>
+ <div class="col-md-6 log-content-wrapper">
+ <div class="collapse log-actions" attr.id="details-{{i}}">
+ <span class="action-icon fa fa-search"></span>
+ <span class="action-icon fa fa-external-link"></span>
+ <span class="action-icon fa fa-bullseye"></span>
+ </div>
+ <div class="log-content-inner-wrapper">
+ <div class="log-content" *ngIf="isColumnDisplayed('log_message')">{{log.log_message}}</div>
+ </div>
+ </div>
+ <div *ngFor="let column of displayedColumns">
+ <div *ngIf="customStyledColumns.indexOf(column.name) === -1" [innerHTML]="log[column.name]" class="col-md-1"></div>
+ </div>
+ </ng-template>
+</accordion-panel>
+<pagination class="col-md-12" *ngIf="logs && logs.length" [totalCount]="totalCount" [filtersForm]="filtersForm"
+ [filterInstance]="filters.pageSize" [currentCount]="logs.length"></pagination>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
new file mode 100644
index 0000000..91d796f
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.less
@@ -0,0 +1,138 @@
+/**
+ * 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';
+
+.logs-header {
+ // TODO get rid of magic numbers, base on actual design
+ margin: 10px 0;
+ padding: 5px 0;
+ background-color: @list-header-background-color; // TODO implement actual color
+ overflow: hidden;
+ text-transform: uppercase;
+}
+
+/deep/ filter-dropdown {
+ justify-content: flex-end;
+}
+
+.hexagon {
+ // TODO get rid of magic numbers, base on actual design
+ left: -7.5px;
+
+ &.fatal {
+ .common-hexagon(15px, @fatal-color);
+ }
+
+ &.error {
+ .common-hexagon(15px, @error-color);
+ }
+
+ &.warn {
+ .common-hexagon(15px, @warning-color);
+ }
+
+ &.info {
+ .common-hexagon(15px, @info-color);
+ }
+
+ &.debug {
+ .common-hexagon(15px, @debug-color);
+ }
+
+ &.trace {
+ .common-hexagon(15px, @trace-color);
+ }
+
+ &.unknown {
+ .common-hexagon(15px, @unknown-color);
+ }
+}
+
+.log-status {
+ text-transform: uppercase;
+
+ &.fatal {
+ color: @fatal-color;
+ }
+
+ &.error {
+ color: @error-color;
+ }
+
+ &.warn {
+ color: @warning-color;
+ }
+
+ &.info {
+ color: @info-color;
+ }
+
+ &.debug {
+ color: @debug-color;
+ }
+
+ &.trace {
+ color: @trace-color;
+ }
+
+ &.unknown {
+ color: @unknown-color;
+ }
+}
+
+.log-type {
+ color: @link-color;
+}
+
+.log-time {
+ color: @grey-color;
+}
+
+.log-content-wrapper {
+ position: relative;
+
+ // TODO get rid of magic numbers, base on actual design
+ .log-content-inner-wrapper {
+ overflow: hidden;
+ max-height: @default-line-height * 2em;
+ padding-right: 65px;
+
+ .log-content {
+ white-space: pre-wrap;
+ }
+ }
+
+ .log-actions {
+ position: absolute;
+ right: 40px;
+ top: 0;
+ border: @input-border;
+
+ &.collapsing + .log-content-inner-wrapper, &.collapse.in + .log-content-inner-wrapper {
+ min-height: 6em;
+ max-height: none;
+ overflow-x: auto;
+ }
+
+ .action-icon {
+ .clickable-item;
+ display: block;
+ padding: 5px;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
new file mode 100644
index 0000000..02c3b23
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.spec.ts
@@ -0,0 +1,102 @@
+/**
+ * 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 {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+import {StoreModule} from '@ngrx/store';
+import {MomentModule} from 'angular2-moment';
+import {MomentTimezoneModule} from 'angular-moment-timezone';
+import {AuditLogsService, auditLogs} from '@app/services/storage/audit-logs.service';
+import {ServiceLogsService, serviceLogs} from '@app/services/storage/service-logs.service';
+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';
+import {HostsService, hosts} from '@app/services/storage/hosts.service';
+import {HttpClientService} from '@app/services/http-client.service';
+import {FilteringService} from '@app/services/filtering.service';
+import {UtilsService} from '@app/services/utils.service';
+
+import {LogsListComponent} from './logs-list.component';
+
+export function HttpLoaderFactory(http: Http) {
+ return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('LogsListComponent', () => {
+ let component: LogsListComponent;
+ let fixture: ComponentFixture<LogsListComponent>;
+ const httpClient = {
+ get: () => {
+ return {
+ subscribe: () => {
+ }
+ };
+ }
+ };
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [LogsListComponent],
+ imports: [
+ StoreModule.provideStore({
+ auditLogs,
+ serviceLogs,
+ appSettings,
+ clusters,
+ components,
+ hosts
+ }),
+ MomentModule,
+ MomentTimezoneModule,
+ TranslateModule.forRoot({
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [Http]
+ })
+ ],
+ providers: [
+ {
+ provide: HttpClientService,
+ useValue: httpClient
+ },
+ AuditLogsService,
+ ServiceLogsService,
+ AppSettingsService,
+ ClustersService,
+ ComponentsService,
+ HostsService,
+ FilteringService,
+ UtilsService
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LogsListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
new file mode 100644
index 0000000..6d73dcb
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/logs-list/logs-list.component.ts
@@ -0,0 +1,62 @@
+/**
+ * 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 'rxjs/add/operator/map';
+import {FilteringService} from '@app/services/filtering.service';
+
+@Component({
+ selector: 'logs-list',
+ templateUrl: './logs-list.component.html',
+ styleUrls: ['./logs-list.component.less']
+})
+export class LogsListComponent {
+
+ constructor(private filtering: FilteringService) {
+ }
+
+ @Input()
+ logs: any[] = [];
+
+ @Input()
+ totalCount: number = 0;
+
+ @Input()
+ displayedColumns: any[] = [];
+
+ readonly customStyledColumns = ['level', 'type', 'logtime', 'log_message'];
+
+ timeFormat: string = 'DD/MM/YYYY HH:mm:ss';
+
+ get timeZone(): string {
+ return this.filtering.timeZone;
+ }
+
+ get filters(): any {
+ return this.filtering.filters;
+ }
+
+ get filtersForm(): FormGroup {
+ return this.filtering.filtersForm;
+ }
+
+ isColumnDisplayed(key: string): boolean {
+ return this.displayedColumns.some(column => column.name === key);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
new file mode 100644
index 0000000..69b3887
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.html
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+
+<ng-template [ngTemplateOutlet]="template"></ng-template>
+<div *ngIf="isInitialLoading" class="text-center">
+ <span class="fa fa-spinner fa-spin"></span>
+</div>
+<login-form *ngIf="!isInitialLoading && !isAuthorized"></login-form>
+<filters-panel *ngIf="isAuthorized" class="row"></filters-panel>
+<logs-container *ngIf="isAuthorized" logsType="serviceLogs"></logs-container>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
new file mode 100644
index 0000000..9736628
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.less
@@ -0,0 +1,24 @@
+/**
+ * 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 {
+ .full-size;
+ overflow-x: hidden;
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
new file mode 100644
index 0000000..42fba68
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.spec.ts
@@ -0,0 +1,65 @@
+/**
+ * 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 {HttpModule} from '@angular/http';
+import {StoreModule} from '@ngrx/store';
+import {AppStateService, appState} from '@app/services/storage/app-state.service';
+import {AuditLogsFieldsService, auditLogsFields} from '@app/services/storage/audit-logs-fields.service';
+import {ServiceLogsFieldsService, serviceLogsFields} from '@app/services/storage/service-logs-fields.service';
+import {HttpClientService} from '@app/services/http-client.service';
+
+import {MainContainerComponent} from './main-container.component';
+
+describe('MainContainerComponent', () => {
+ let component: MainContainerComponent;
+ let fixture: ComponentFixture<MainContainerComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [MainContainerComponent],
+ imports: [
+ HttpModule,
+ StoreModule.provideStore({
+ appState,
+ auditLogsFields,
+ serviceLogsFields
+ })
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ providers: [
+ AppStateService,
+ AuditLogsFieldsService,
+ ServiceLogsFieldsService,
+ HttpClientService
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MainContainerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
new file mode 100644
index 0000000..53d58cf
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/main-container/main-container.component.ts
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {Component, ContentChild, TemplateRef} from '@angular/core';
+import {HttpClientService} from '@app/services/http-client.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
+import {AuditLogsFieldsService} from '@app/services/storage/audit-logs-fields.service';
+import {ServiceLogsFieldsService} from '@app/services/storage/service-logs-fields.service';
+import {AuditLogField} from '@app/models/audit-log-field.model';
+import {ServiceLogField} from '@app/models/service-log-field.model';
+
+@Component({
+ selector: 'main-container',
+ templateUrl: './main-container.component.html',
+ styleUrls: ['./main-container.component.less']
+})
+export class MainContainerComponent {
+
+ constructor(private httpClient: HttpClientService, private appState: AppStateService, private auditLogsFieldsStorage: AuditLogsFieldsService, private serviceLogsFieldsStorage: ServiceLogsFieldsService) {
+ this.loadColumnsNames();
+ appState.getParameter('isAuthorized').subscribe(value => this.isAuthorized = value);
+ appState.getParameter('isInitialLoading').subscribe(value => this.isInitialLoading = value);
+ }
+
+ @ContentChild(TemplateRef)
+ template;
+
+ isAuthorized: boolean = false;
+
+ isInitialLoading: boolean = false;
+
+ private loadColumnsNames(): void {
+ this.httpClient.get('serviceLogsFields').subscribe(response => {
+ const jsonResponse = response.json();
+ if (jsonResponse) {
+ this.serviceLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, ServiceLogField));
+ }
+ });
+ this.httpClient.get('auditLogsFields').subscribe(response => {
+ const jsonResponse = response.json();
+ if (jsonResponse) {
+ this.auditLogsFieldsStorage.addInstances(this.getColumnsArray(jsonResponse, AuditLogField));
+ }
+ });
+ }
+
+ private getColumnsArray(keysObject: any, fieldClass: any): any[] {
+ return Object.keys(keysObject).map(key => new fieldClass(key));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
new file mode 100644
index 0000000..2f05656
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
@@ -0,0 +1,28 @@
+<!--
+ 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 #dropdown [ngClass]="{'dropdown': hasSubItems, 'text-center': true}">
+ <a [ngClass]="iconClass + ' icon'" (mousedown)="onMouseDown($event)" (mouseup)="onMouseUp($event)"
+ (click)="$event.stopPropagation()"></a>
+ <a #dropdownToggle class="dropdown-toggle caret" data-toggle="dropdown" *ngIf="hasCaret"></a>
+ <br>
+ <a *ngIf="label" (mousedown)="onMouseDown($event)" [ngClass]="labelClass" (mouseup)="onMouseUp($event)"
+ (click)="$event.stopPropagation()">{{label | translate}}</a>
+ <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems" (selectedItemChange)="updateValue($event)"
+ [isMultipleChoice]="isMultipleChoice" [additionalLabelComponentSetter]="additionalLabelComponentSetter"
+ [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right': isRightAlign}"></ul>
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
new file mode 100644
index 0000000..6a3a43d
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.less
@@ -0,0 +1,33 @@
+/**
+ * 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 {
+ display: inline-block;
+ cursor: pointer;
+
+ a:hover, a:focus {
+ text-decoration: none;
+ }
+
+ .icon {
+ padding: 5px;
+ }
+
+ .unstyled-link {
+ color: inherit;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/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
new file mode 100644
index 0000000..6c9e021
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
@@ -0,0 +1,133 @@
+/**
+ * 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 {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+import {StoreModule} from '@ngrx/store';
+import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {ComponentActionsService} from '@app/services/component-actions.service';
+import {FilteringService} from '@app/services/filtering.service';
+
+import {MenuButtonComponent} from './menu-button.component';
+
+export function HttpLoaderFactory(http: Http) {
+ return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('MenuButtonComponent', () => {
+ let component: MenuButtonComponent;
+ let fixture: ComponentFixture<MenuButtonComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [MenuButtonComponent],
+ imports: [
+ StoreModule.provideStore({
+ appSettings
+ }),
+ TranslateModule.forRoot({
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [Http]
+ })
+ ],
+ providers: [
+ AppSettingsService,
+ ComponentActionsService,
+ FilteringService
+ ],
+ schemas: [NO_ERRORS_SCHEMA]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MenuButtonComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+
+ describe('#hasSubItems', () => {
+ const cases = [
+ {
+ subItems: null,
+ hasSubItems: false,
+ title: 'no sub-items'
+ },
+ {
+ subItems: [],
+ hasSubItems: false,
+ title: 'empty sub-items array'
+ },
+ {
+ subItems: [{}],
+ hasSubItems: true,
+ title: 'sub-items present'
+ }
+ ];
+
+ cases.forEach((test) => {
+ it(test.title, () => {
+ component.subItems = test.subItems;
+ expect(component.hasSubItems).toEqual(test.hasSubItems);
+ });
+ });
+ });
+
+ describe('#hasCaret', () => {
+ const cases = [
+ {
+ subItems: null,
+ hasCaret: false,
+ title: 'no sub-items'
+ },
+ {
+ subItems: [],
+ hasCaret: false,
+ title: 'empty sub-items array'
+ },
+ {
+ subItems: [{}],
+ hideCaret: false,
+ hasCaret: true,
+ title: 'sub-items present, caret not hidden'
+ },
+ {
+ subItems: [{}],
+ hideCaret: true,
+ hasCaret: true,
+ title: 'sub-items present, caret hidden'
+ }
+ ];
+
+ cases.forEach((test) => {
+ it(test.title, () => {
+ component.subItems = test.subItems;
+ component.hideCaret = Boolean(test.hideCaret);
+ expect(component.hasSubItems).toEqual(test.hasCaret);
+ });
+ });
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/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
new file mode 100644
index 0000000..b674ec6
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
@@ -0,0 +1,97 @@
+/**
+ * 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, ViewChild, ElementRef} from '@angular/core';
+import {ComponentActionsService} from '@app/services/component-actions.service';
+import * as $ from 'jquery';
+
+@Component({
+ selector: 'menu-button',
+ templateUrl: './menu-button.component.html',
+ styleUrls: ['./menu-button.component.less']
+})
+export class MenuButtonComponent {
+
+ constructor(protected actions: ComponentActionsService) {
+ }
+
+ @ViewChild('dropdown')
+ dropdown: ElementRef;
+
+ @Input()
+ label?: string;
+
+ @Input()
+ action: string;
+
+ @Input()
+ iconClass: string;
+
+ @Input()
+ labelClass?: string;
+
+ @Input()
+ subItems?: any[];
+
+ @Input()
+ isMultipleChoice: boolean = false;
+
+ @Input()
+ hideCaret: boolean = false;
+
+ @Input()
+ isRightAlign: boolean = false;
+
+ @Input()
+ additionalLabelComponentSetter?: string;
+
+ get hasSubItems(): boolean {
+ return Boolean(this.subItems && this.subItems.length);
+ }
+
+ get hasCaret(): boolean {
+ return this.hasSubItems && !this.hideCaret;
+ }
+
+ private clickStartTime: number;
+
+ private readonly longClickInterval = 1000;
+
+ onMouseDown(event: MouseEvent): void {
+ if (this.action && event.button === 0) {
+ this.clickStartTime = (new Date()).getTime();
+ }
+ }
+
+ onMouseUp(event: MouseEvent): void {
+ if (event.button === 0) {
+ const clickEndTime = (new Date()).getTime();
+ if (this.hasSubItems && (!this.action || clickEndTime - this.clickStartTime >= this.longClickInterval)) {
+ $(this.dropdown.nativeElement).toggleClass('open');
+ } else if (this.action) {
+ this.actions[this.action]();
+ }
+ event.stopPropagation();
+ }
+ }
+
+ updateValue(options: any) {
+ // TODO implement value change behaviour
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html
new file mode 100644
index 0000000..abd7bc8
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html
@@ -0,0 +1,40 @@
+<!--
+ 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="modal-backdrop in"></div>
+<div class="modal in">
+ <div [ngClass]="{'modal-dialog': true, 'modal-sm': isSmallModal, 'modal-lg': isLargeModal}">
+ <div class="modal-content">
+ <div *ngIf="showHeader" class="modal-header">
+ <button *ngIf="showCloseButton" type="button" class="close" data-dismiss="modal" (click)="onClose()">
+ <span>×</span>
+ </button>
+ <h4 *ngIf="title">{{title}}</h4>
+ </div>
+ <div class="modal-body">
+ <div *ngIf="bodyText">{{bodyText}}</div>
+ <ng-template *ngIf="bodyTemplate" [ngTemplateOutlet]="bodyTemplate"></ng-template>
+ </div>
+ <div *ngIf="showFooter" class="modal-footer">
+ <button *ngIf="showCancelButton" class="btn {{cancelButtonClassName}}"
+ (click)="onCancel()">{{cancelButtonLabel | translate}}</button>
+ <button *ngIf="showSubmitButton" class="btn {{submitButtonClassName}}"
+ (click)="onSubmit()">{{submitButtonLabel | translate}}</button>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts
new file mode 100644
index 0000000..802bd13
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.spec.ts
@@ -0,0 +1,57 @@
+/**
+ * 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 {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+
+import {ModalComponent} from './modal.component';
+
+export function HttpLoaderFactory(http: Http) {
+ return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('ModalComponent', () => {
+ let component: ModalComponent;
+ let fixture: ComponentFixture<ModalComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ModalComponent],
+ imports: [
+ TranslateModule.forRoot({
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [Http]
+ })
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ModalComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts
new file mode 100644
index 0000000..32f59f6
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.ts
@@ -0,0 +1,122 @@
+/**
+ * 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, OnInit, AfterViewInit, ElementRef, Input, Output, ContentChild, TemplateRef, EventEmitter} from '@angular/core';
+import * as $ from 'jquery';
+
+@Component({
+ selector: 'modal',
+ templateUrl: './modal.component.html'
+})
+export class ModalComponent implements OnInit, AfterViewInit {
+
+ constructor(private element: ElementRef) {
+ this.rootElement = $(this.element.nativeElement);
+ }
+
+ ngOnInit() {
+ this.modalElements = this.rootElement.find('.in');
+ this.show();
+ }
+
+ ngAfterViewInit() {
+ this.init.emit();
+ }
+
+ private rootElement: JQuery;
+
+ private modalElements: JQuery;
+
+ @Input()
+ showHeader: boolean = true;
+
+ @Input()
+ title: string = '';
+
+ @Input()
+ showCloseButton: boolean = true;
+
+ @Input()
+ bodyText: string = '';
+
+ @Input()
+ showFooter: boolean = true;
+
+ @Input()
+ showSubmitButton: boolean = true;
+
+ @Input()
+ submitButtonLabel: string = 'modal.submit';
+
+ @Input()
+ submitButtonClassName: string = 'btn-success';
+
+ @Input()
+ showCancelButton: boolean = true;
+
+ @Input()
+ cancelButtonLabel: string = 'modal.cancel';
+
+ @Input()
+ cancelButtonClassName: string = 'btn-default';
+
+ @Input()
+ isSmallModal: boolean = false;
+
+ @Input()
+ isLargeModal: boolean = false;
+
+ @ContentChild(TemplateRef)
+ bodyTemplate;
+
+ @Output()
+ init: EventEmitter<any> = new EventEmitter();
+
+ @Output()
+ submit: EventEmitter<any> = new EventEmitter();
+
+ @Output()
+ cancel: EventEmitter<any> = new EventEmitter();
+
+ @Output()
+ close: EventEmitter<any> = new EventEmitter();
+
+ show(): void {
+ this.modalElements.show();
+ }
+
+ hide(): void {
+ this.modalElements.hide();
+ }
+
+ onSubmit(): void {
+ this.hide();
+ this.submit.emit();
+ }
+
+ onCancel(): void {
+ this.hide();
+ this.cancel.emit();
+ }
+
+ onClose(): void {
+ this.hide();
+ this.close.emit();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.html
new file mode 100644
index 0000000..96c8619
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.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.
+-->
+
+<div *ngFor="let item of data" class="bar-sector"
+ [ngStyle]="{'background-color': item.color, 'width': (item.value / totalCount * 100) + '%'}"></div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less
new file mode 100644
index 0000000..b78b847
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.less
@@ -0,0 +1,39 @@
+/**
+ * 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.
+ */
+
+@bar-height: 8px;
+
+:host {
+ display: block;
+ width: 100%;
+
+ .bar-sector {
+ display: inline-block;
+ height: @bar-height;
+
+ &:first-child {
+ border-top-left-radius: @bar-height / 2;
+ border-bottom-left-radius: @bar-height / 2;
+ }
+
+ &:last-child {
+ border-top-right-radius: @bar-height / 2;
+ border-bottom-right-radius: @bar-height / 2;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts
new file mode 100644
index 0000000..d47436e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.spec.ts
@@ -0,0 +1,43 @@
+/**
+ * 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 {NodeBarComponent} from './node-bar.component';
+
+describe('NodeBarComponent', () => {
+ let component: NodeBarComponent;
+ let fixture: ComponentFixture<NodeBarComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [NodeBarComponent]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NodeBarComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts
new file mode 100644
index 0000000..c7b3ead
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/node-bar/node-bar.component.ts
@@ -0,0 +1,35 @@
+/**
+ * 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: 'node-bar',
+ templateUrl: './node-bar.component.html',
+ styleUrls: ['./node-bar.component.less']
+})
+export class NodeBarComponent {
+
+ @Input()
+ data: any[] = [];
+
+ get totalCount(): number {
+ return this.data.reduce((currentValue, currentItem) => currentValue + Number(currentItem.value), 0);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html
new file mode 100644
index 0000000..c227a2b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.html
@@ -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.
+-->
+
+<button class="btn btn-link" [disabled]="currentPage === 0" (click)="updateValue(true)">
+ <span class="pagination-control fa fa-chevron-left"></span>
+</button>
+<button class="btn btn-link" [disabled]="currentPage === pagesCount - 1" (click)="updateValue()">
+ <span class="pagination-control fa fa-chevron-right"></span>
+</button>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less
new file mode 100644
index 0000000..8238eaf
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.less
@@ -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 '../variables';
+
+.pagination-control {
+ .clickable-item;
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts
new file mode 100644
index 0000000..489f79c
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.spec.ts
@@ -0,0 +1,43 @@
+/**
+ * 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 {PaginationControlsComponent} from './pagination-controls.component';
+
+describe('PaginationControlsComponent', () => {
+ let component: PaginationControlsComponent;
+ let fixture: ComponentFixture<PaginationControlsComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [PaginationControlsComponent]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PaginationControlsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts
new file mode 100644
index 0000000..c71844c
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination-controls/pagination-controls.component.ts
@@ -0,0 +1,73 @@
+/**
+ * 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, forwardRef, Input, Output, EventEmitter} from '@angular/core';
+import {ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+
+@Component({
+ selector: 'pagination-controls',
+ templateUrl: './pagination-controls.component.html',
+ styleUrls: ['./pagination-controls.component.less'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => PaginationControlsComponent),
+ multi: true
+ }
+ ]
+})
+export class PaginationControlsComponent implements ControlValueAccessor {
+
+ private onChange: (fn: any) => void;
+
+ currentPage: number = 0;
+
+ @Input()
+ totalCount: number;
+
+ @Input()
+ pagesCount: number;
+
+ @Output()
+ currentPageChange: EventEmitter<number> = new EventEmitter();
+
+ get value(): number {
+ return this.currentPage;
+ }
+
+ set value(newValue: number) {
+ this.currentPage = newValue;
+ this.currentPageChange.emit(newValue);
+ this.onChange(newValue);
+ }
+
+ updateValue(isDecrement?: boolean) {
+ isDecrement? this.value-- : this.value++;
+ }
+
+ writeValue() {
+ }
+
+ registerOnChange(callback: any): void {
+ this.onChange = callback;
+ }
+
+ registerOnTouched() {
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html
new file mode 100644
index 0000000..be6591b
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.html
@@ -0,0 +1,24 @@
+<!--
+ 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.
+-->
+
+<form class="pagination-form col-md-12" [formGroup]="filtersForm">
+ <filter-dropdown [label]="filterInstance.label" formControlName="pageSize" [options]="filterInstance.options"
+ [defaultLabel]="filterInstance.defaultLabel" [isRightAlign]="true" isDropup="true"></filter-dropdown>
+ <span>{{'pagination.numbers' | translate: numbersTranslateParams}}</span>
+ <pagination-controls formControlName="page" [totalCount]="totalCount" [pagesCount]="pagesCount"
+ (currentPageChange)="setCurrentPage($event)"></pagination-controls>
+</form>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less
new file mode 100644
index 0000000..df8ad2d
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.less
@@ -0,0 +1,28 @@
+/**
+ * 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 {
+ display: flex;
+
+ .pagination-form {
+ .flex-vertical-align;
+ justify-content: flex-end;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts
new file mode 100644
index 0000000..7a15bbc
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.spec.ts
@@ -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 {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {Http} from '@angular/http';
+import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
+import {TranslateHttpLoader} from '@ngx-translate/http-loader';
+
+import {PaginationComponent} from './pagination.component';
+
+export function HttpLoaderFactory(http: Http) {
+ return new TranslateHttpLoader(http, 'assets/i18n/', '.json');
+}
+
+describe('PaginationComponent', () => {
+ let component: PaginationComponent;
+ let fixture: ComponentFixture<PaginationComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ TranslateModule.forRoot({
+ provide: TranslateLoader,
+ useFactory: HttpLoaderFactory,
+ deps: [Http]
+ })
+ ],
+ declarations: [PaginationComponent],
+ schemas: [NO_ERRORS_SCHEMA]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PaginationComponent);
+ component = fixture.componentInstance;
+ component.filterInstance = {};
+ component.filtersForm = {
+ controls: {
+ pageSize: {
+ valueChanges: {
+ subscribe: () => {}
+ }
+ }
+ }
+ };
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts
new file mode 100644
index 0000000..d38d0d8
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/pagination/pagination.component.ts
@@ -0,0 +1,72 @@
+/**
+ * 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, OnInit, Input} from '@angular/core';
+import {FormGroup} from '@angular/forms';
+
+@Component({
+ selector: 'pagination',
+ templateUrl: './pagination.component.html',
+ styleUrls: ['./pagination.component.less']
+})
+export class PaginationComponent implements OnInit {
+
+ ngOnInit() {
+ this.setPageSizeFromString(this.filterInstance.defaultValue);
+ this.filtersForm.controls.pageSize.valueChanges.subscribe(value => this.setPageSizeFromString(value));
+ }
+
+ @Input()
+ filtersForm: FormGroup;
+
+ @Input()
+ filterInstance: any;
+
+ @Input()
+ currentCount?: number;
+
+ @Input()
+ totalCount: number;
+
+ private pageSize: number = 0;
+
+ setPageSizeFromString(value: string) {
+ this.pageSize = parseInt(value);
+ }
+
+ private currentPage: number = 0;
+
+ get numbersTranslateParams(): any {
+ const pageSize = this.pageSize,
+ startIndex = (this.currentPage * pageSize) + 1;
+ return {
+ startIndex,
+ endIndex: startIndex + Math.min(pageSize, this.currentCount) - 1,
+ totalCount: this.totalCount
+ }
+ }
+
+ get pagesCount(): number {
+ return Math.ceil(this.totalCount / this.pageSize);
+ }
+
+ setCurrentPage(pageNumber: number) {
+ this.currentPage = pageNumber;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/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
new file mode 100644
index 0000000..299e46e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.html
@@ -0,0 +1,18 @@
+<!--
+ 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 #container></div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/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
new file mode 100644
index 0000000..d891862
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.less
@@ -0,0 +1,29 @@
+/**
+ * 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.
+ */
+
+/deep/ .axis {
+ .domain {
+ display: none;
+ }
+
+ .tick {
+ line {
+ display: none;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/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
new file mode 100644
index 0000000..9e056be
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.spec.ts
@@ -0,0 +1,53 @@
+/**
+ * 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 {StoreModule} from '@ngrx/store';
+import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+
+import {TimeHistogramComponent} from './time-histogram.component';
+
+describe('TimeHistogramComponent', () => {
+ let component: TimeHistogramComponent;
+ let fixture: ComponentFixture<TimeHistogramComponent>;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [TimeHistogramComponent],
+ imports: [
+ StoreModule.provideStore({
+ appSettings
+ })
+ ],
+ providers: [
+ AppSettingsService
+ ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TimeHistogramComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create component', () => {
+ expect(component).toBeTruthy();
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/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
new file mode 100644
index 0000000..7856ecc
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-histogram/time-histogram.component.ts
@@ -0,0 +1,161 @@
+/**
+ * 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, OnInit, AfterViewInit, OnChanges, Input, ViewChild, ElementRef} from '@angular/core';
+import * as d3 from 'd3';
+import * as moment from 'moment-timezone';
+import {AppSettingsService} from '@app/services/storage/app-settings.service';
+
+@Component({
+ selector: 'time-histogram',
+ templateUrl: './time-histogram.component.html',
+ styleUrls: ['./time-histogram.component.less']
+})
+export class TimeHistogramComponent implements OnInit, AfterViewInit, OnChanges {
+
+ constructor(private appSettings: AppSettingsService) {
+ appSettings.getParameter('timeZone').subscribe(value => {
+ this.timeZone = value;
+ this.createHistogram();
+ });
+ }
+
+ ngOnInit() {
+ Object.assign(this.options, this.defaultOptions, this.customOptions);
+ }
+
+ ngAfterViewInit() {
+ this.htmlElement = this.element.nativeElement;
+ this.host = d3.select(this.htmlElement);
+ }
+
+ ngOnChanges() {
+ this.createHistogram();
+ }
+
+ @ViewChild('container')
+ element: ElementRef;
+
+ @Input()
+ customOptions: any;
+
+ @Input()
+ data: any;
+
+ private readonly defaultOptions = {
+ margin: {
+ top: 20,
+ right: 20,
+ bottom: 40,
+ left: 50
+ },
+ height: 200,
+ tickPadding: 10,
+ columnWidth: 20
+ };
+
+ private options: any = {};
+
+ private timeZone: string;
+
+ private host;
+
+ private svg;
+
+ private width;
+
+ private xScale;
+
+ private yScale;
+
+ private colorScale;
+
+ private xAxis;
+
+ private yAxis;
+
+ private htmlElement: HTMLElement;
+
+ histogram: any;
+
+ 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, key) => [...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('width', this.width + margin.left + margin.right)
+ .attr('height', this.options.height + margin.top + margin.bottom).append('g')
+ .attr('transform', `translate(${margin.left},${margin.top})`);
+ }
+
+ private drawXAxis(): void {
+ this.xAxis = d3.axisBottom(this.xScale)
+ .tickFormat(tick => moment(tick).tz(this.timeZone).format('MM/DD HH:mm'))
+ .tickPadding(this.options.tickPadding);
+ this.svg.append('g').attr('class', 'axis').attr('transform', `translate(0,${this.options.height})`).call(this.xAxis);
+ }
+
+ private drawYAxis(): void {
+ this.yAxis = d3.axisLeft(this.yScale).tickFormat((tick: number) => {
+ if (Number.isInteger(tick)) {
+ return tick.toFixed(0);
+ } else {
+ return;
+ }
+ }).tickPadding(this.options.tickPadding);
+ this.svg.append('g').attr('class', 'axis').call(this.yAxis).append('text');
+ }
+
+ private populate(): void {
+ const keys = Object.keys(this.options.keysWithColors),
+ data = this.data,
+ timeStamps = Object.keys(data),
+ formattedData = timeStamps.map(timeStamp => Object.assign({
+ timeStamp: timeStamp
+ }, data[timeStamp])),
+ layers = (d3.stack().keys(keys)(formattedData)),
+ columnWidth = this.options.columnWidth;
+ this.xScale.domain(d3.extent(formattedData, item => item.timeStamp));
+ this.yScale.domain([0, d3.max(formattedData, item => keys.reduce((sum, key) => sum + item[key], 0))]);
+ this.drawXAxis();
+ this.drawYAxis();
+ const layer = this.svg.selectAll().data(d3.transpose<any>(layers)).enter().append('g');
+ layer.selectAll().data(item => item).enter().append('rect')
+ .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));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html
new file mode 100644
index 0000000..3cb196e
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.html
@@ -0,0 +1,26 @@
+<!--
+ 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.
+-->
+
+<button class="btn btn-link" (click)="setTimeZonePickerDisplay(true)">
+ {{timeZone | timeZoneAbbr}} <span class="caret"></span>
+</button>
+<modal *ngIf="isTimeZonePickerDisplayed" [showCloseButton]="false" [isLargeModal]="true"
+ (init)="initMap()" (cancel)="setTimeZonePickerDisplay(false)" (submit)="setTimeZone()">
+ <ng-template>
+ <div attr.id="{{mapElementId}}"></div>
+ </ng-template>
+</modal>
http://git-wip-us.apache.org/repos/asf/ambari/blob/02360dd5/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less
new file mode 100644
index 0000000..4fa043d
--- /dev/null
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.less
@@ -0,0 +1,45 @@
+/**
+ * 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';
+
+.btn-link {
+ // TODO implement actual colors
+ color: @submit-color;
+
+ &:hover {
+ color: @submit-hover-color;
+ }
+}
+
+/deep/ #timezone-map {
+ .Cbox {
+ .quickLink {
+ padding-top: 4px;
+ }
+ }
+
+ .hoverZone {
+ display: inline-block;
+
+ &:after {
+ content: '\007C\00a0\00a0';
+ visibility: hidden;
+ }
+ }
+}