AMBARI-22054 Log Search UI: implement 'Capture' functionality. (ababiichuk)
authorababiichuk <ababiichuk@hortonworks.com>
Mon, 25 Sep 2017 17:00:41 +0000 (20:00 +0300)
committerababiichuk <ababiichuk@hortonworks.com>
Mon, 25 Sep 2017 17:00:41 +0000 (20:00 +0300)
32 files changed:
ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
ambari-logsearch/ambari-logsearch-web/src/app/classes/queries/audit-logs-query-params.class.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.less
ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.html
ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.less
ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/logs-container/logs-container.component.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.html
ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/modal/modal.component.html
ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.html
ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.html
ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.html
ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/components/variables.less
ambari-logsearch/ambari-logsearch-web/src/app/pipes/timer-seconds.pipe.spec.ts [new file with mode: 0644]
ambari-logsearch/ambari-logsearch-web/src/app/pipes/timer-seconds.pipe.ts [new file with mode: 0644]
ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
ambari-logsearch/ambari-logsearch-web/src/app/services/filtering.service.ts
ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
ambari-logsearch/ambari-logsearch-web/src/app/services/mock-api-data.service.ts
ambari-logsearch/ambari-logsearch-web/src/app/services/utils.service.ts
ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json

index fca68b5..c4dc698 100644 (file)
@@ -78,8 +78,9 @@ import {TimeRangePickerComponent} from '@app/components/time-range-picker/time-r
 import {DatePickerComponent} from '@app/components/date-picker/date-picker.component';
 
 import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe';
+import {TimerSecondsPipe} from '@app/pipes/timer-seconds.pipe';
 
-export function HttpLoaderFactory(http: Http) {
+export function HttpLoaderFactory(http: Http): TranslateHttpLoader {
   // adding 'static' parameter to step over mock data request
   return new TranslateHttpLoader(http, 'resources/assets/i18n/', '.json?static=true');
 }
@@ -123,7 +124,8 @@ export function getXHRBackend(injector: Injector, browser: BrowserXhr, xsrf: XSR
     SearchBoxComponent,
     TimeRangePickerComponent,
     DatePickerComponent,
-    TimeZoneAbbrPipe
+    TimeZoneAbbrPipe,
+    TimerSecondsPipe
   ],
   imports: [
     BrowserModule,
index d179376..e36bf18 100644 (file)
@@ -36,8 +36,6 @@ export class AuditLogsQueryParams extends QueryParams {
   startIndex: string;
   sortBy?: string;
   sortType?: string;
-  start_time?: string;
-  end_time?: string;
   clusters?: string;
   mustBe?: string;
   mustNot?: string;
index 04730ef..f7227b1 100644 (file)
@@ -25,10 +25,16 @@ import {ClustersService, clusters} from '@app/services/storage/clusters.service'
 import {ComponentsService, components} from '@app/services/storage/components.service';
 import {AppStateService, appState} from '@app/services/storage/app-state.service';
 import {HostsService, hosts} from '@app/services/storage/hosts.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 {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
 import {HttpClientService} from '@app/services/http-client.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
 
 import {DropdownButtonComponent} from './dropdown-button.component';
 
@@ -45,7 +51,12 @@ describe('DropdownButtonComponent', () => {
           clusters,
           components,
           appState,
-          hosts
+          hosts,
+          auditLogs,
+          auditLogsFields,
+          serviceLogs,
+          serviceLogsFields,
+          serviceLogsHistogramData
         }),
         ...TranslationModules
       ],
@@ -55,10 +66,16 @@ describe('DropdownButtonComponent', () => {
         ComponentsService,
         AppStateService,
         HostsService,
+        AuditLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
         FilteringService,
         UtilsService,
         ComponentActionsService,
-        HttpClientService
+        HttpClientService,
+        LogsContainerService
       ],
       schemas: [NO_ERRORS_SCHEMA]
     })
index fddf645..a01a3f3 100644 (file)
@@ -25,10 +25,16 @@ import {ClustersService, clusters} from '@app/services/storage/clusters.service'
 import {ComponentsService, components} from '@app/services/storage/components.service';
 import {AppStateService, appState} from '@app/services/storage/app-state.service';
 import {HostsService, hosts} from '@app/services/storage/hosts.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 {ComponentActionsService} from '@app/services/component-actions.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
 import {HttpClientService} from '@app/services/http-client.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
 
 import {FilterButtonComponent} from './filter-button.component';
 
@@ -45,7 +51,12 @@ describe('FilterButtonComponent', () => {
           clusters,
           components,
           appState,
-          hosts
+          hosts,
+          auditLogs,
+          auditLogsFields,
+          serviceLogs,
+          serviceLogsFields,
+          serviceLogsHistogramData
         }),
         ...TranslationModules
       ],
@@ -55,10 +66,16 @@ describe('FilterButtonComponent', () => {
         ComponentsService,
         AppStateService,
         HostsService,
+        AuditLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
         ComponentActionsService,
         FilteringService,
         UtilsService,
-        HttpClientService
+        HttpClientService,
+        LogsContainerService
       ],
       schemas: [NO_ERRORS_SCHEMA]
     })
index 8620607..85e7ecb 100644 (file)
@@ -20,9 +20,17 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing';
 import {TranslationModules} from '@app/test-config.spec';
 import {StoreModule} from '@ngrx/store';
 import {AppSettingsService, appSettings} from '@app/services/storage/app-settings.service';
+import {AppStateService, appState} from '@app/services/storage/app-state.service';
+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 {FilteringService} from '@app/services/filtering.service';
 import {UtilsService} from '@app/services/utils.service';
 import {ComponentActionsService} from '@app/services/component-actions.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {HttpClientService} from '@app/services/http-client.service';
 
 import {FilterDropdownComponent} from './filter-dropdown.component';
 
@@ -51,18 +59,32 @@ describe('FilterDropdownComponent', () => {
       declarations: [FilterDropdownComponent],
       imports: [
         StoreModule.provideStore({
-          appSettings
+          appSettings,
+          appState,
+          auditLogs,
+          auditLogsFields,
+          serviceLogs,
+          serviceLogsFields,
+          serviceLogsHistogramData
         }),
         ...TranslationModules
       ],
       providers: [
         AppSettingsService,
+        AppStateService,
+        AuditLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
         {
           provide: FilteringService,
           useValue: filtering
         },
         UtilsService,
-        ComponentActionsService
+        ComponentActionsService,
+        LogsContainerService,
+        HttpClientService
       ],
       schemas: [NO_ERRORS_SCHEMA]
     })
index 99dd43d..fc3dac8 100644 (file)
   <div class="default-flex col-md-4">
     <dropdown-button [options]="searchBoxItems" iconClass="fa fa-search-minus" label="filter.excluded"
                      [hideCaret]="true" [showSelectedValue]="false" action="proceedWithExclude"></dropdown-button>
-    <filter-button formControlName="hosts" [label]="filters.hosts.label"
+    <filter-button formControlName="hosts" label="{{filters.hosts.label | translate}}"
                    [iconClass]="filters.hosts.iconClass" [subItems]="filters.hosts.options"
                    [isMultipleChoice]="true" [isRightAlign]="true"
                    additionalLabelComponentSetter="getDataForHostsNodeBar"></filter-button>
-    <filter-button formControlName="components" [label]="filters.components.label"
+    <filter-button formControlName="components" label="{{filters.components.label | translate}}"
                    [iconClass]="filters.components.iconClass" [subItems]="filters.components.options"
                    [isMultipleChoice]="true" [isRightAlign]="true"></filter-button>
-    <filter-button formControlName="levels" [label]="filters.levels.label" [iconClass]="filters.levels.iconClass"
+    <filter-button formControlName="levels" label="{{filters.levels.label | translate}}" [iconClass]="filters.levels.iconClass"
                    [subItems]="filters.levels.options" [isMultipleChoice]="true" [isRightAlign]="true"></filter-button>
-    <menu-button label="filter.capture" iconClass="fa fa-caret-right"></menu-button>
+    <menu-button *ngIf="!captureSeconds" label="{{'filter.capture' | translate}}" iconClass="fa fa-caret-right"
+                 action="startCapture"></menu-button>
+    <menu-button *ngIf="captureSeconds" label="{{captureSeconds | timerSeconds}}" iconClass="fa fa-stop stop-icon"
+                 action="stopCapture"></menu-button>
   </div>
 </form>
index fe2e40a..ae5a4af 100644 (file)
@@ -34,6 +34,7 @@ import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
 import {UtilsService} from '@app/services/utils.service';
 import {LogsContainerService} from '@app/services/logs-container.service';
+import {TimerSecondsPipe} from '@app/pipes/timer-seconds.pipe';
 
 import {FiltersPanelComponent} from './filters-panel.component';
 
@@ -51,7 +52,10 @@ describe('FiltersPanelComponent', () => {
       }
     };
     TestBed.configureTestingModule({
-      declarations: [FiltersPanelComponent],
+      declarations: [
+        FiltersPanelComponent,
+        TimerSecondsPipe
+      ],
       imports: [
         StoreModule.provideStore({
           appSettings,
index a43f6c0..5145b76 100644 (file)
   limitations under the License.
 -->
 
+<div *ngIf="autoRefreshRemainingSeconds" class="col-md-12">
+  <div class="auto-refresh-message pull-right">
+    {{'filter.capture.triggeringRefresh' | translate: autoRefreshMessageParams}}
+  </div>
+</div>
 <time-histogram class="col-md-12" [data]="histogramData" [customOptions]="histogramOptions"></time-histogram>
 <dropdown-button class="pull-right" label="logs.columns" [options]="availableColumns | async" [isRightAlign]="true"
                  isMultipleChoice="true" action="updateSelectedColumns"
index 30c6a33..811c6e6 100644 (file)
@@ -19,6 +19,7 @@
 import {async, ComponentFixture, TestBed} from '@angular/core/testing';
 import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
 import {StoreModule} from '@ngrx/store';
+import {TranslationModules} from '@app/test-config.spec';
 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';
@@ -63,7 +64,8 @@ describe('LogsContainerComponent', () => {
           serviceLogsFields,
           serviceLogsHistogramData,
           hosts
-        })
+        }),
+        ...TranslationModules
       ],
       providers: [
         {
index 3e2a6c7..b1fad17 100644 (file)
@@ -102,4 +102,14 @@ export class LogsContainerComponent implements OnInit {
     return this.filtering.filtersForm;
   }
 
+  get autoRefreshRemainingSeconds(): number {
+    return this.filtering.autoRefreshRemainingSeconds;
+  }
+
+  get autoRefreshMessageParams(): any {
+    return {
+      remainingSeconds: this.autoRefreshRemainingSeconds
+    };
+  }
+
 }
index 2f05656..ca70927 100644 (file)
@@ -21,7 +21,7 @@
   <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>
+     (click)="$event.stopPropagation()">{{label}}</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>
index f736fd5..f92961e 100644 (file)
@@ -25,10 +25,15 @@ 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 {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 {ComponentActionsService} from '@app/services/component-actions.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
-import {ListItem} from "@app/classes/list-item.class";
+import {LogsContainerService} from '@app/services/logs-container.service';
 
 import {MenuButtonComponent} from './menu-button.component';
 
@@ -45,7 +50,12 @@ describe('MenuButtonComponent', () => {
           appState,
           clusters,
           components,
-          hosts
+          hosts,
+          auditLogs,
+          auditLogsFields,
+          serviceLogs,
+          serviceLogsFields,
+          serviceLogsHistogramData
         }),
         ...TranslationModules
       ],
@@ -55,9 +65,15 @@ describe('MenuButtonComponent', () => {
         ClustersService,
         ComponentsService,
         HostsService,
+        AuditLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
         ComponentActionsService,
         FilteringService,
-        HttpClientService
+        HttpClientService,
+        LogsContainerService
       ],
       schemas: [NO_ERRORS_SCHEMA]
     })
index abd7bc8..398a429 100644 (file)
@@ -21,7 +21,7 @@
     <div class="modal-content">
       <div *ngIf="showHeader" class="modal-header">
         <button *ngIf="showCloseButton" type="button" class="close" data-dismiss="modal" (click)="onClose()">
-          <span>&times;</span>
+          <span class="fa fa-times"></span>
         </button>
         <h4 *ngIf="title">{{title}}</h4>
       </div>
index e88159c..92f9520 100644 (file)
@@ -19,7 +19,7 @@
   <span *ngIf="parameter.isExclude" class="fa fa-search-minus exclude-icon"></span>
   {{parameter.label | translate}}:
   <span class="parameter-value">{{parameter.value}}</span>
-  <span class="remove-parameter" (click)="removeParameter($event, parameter.id)">&times;</span>
+  <span class="fa fa-times remove-parameter" (click)="removeParameter($event, parameter.id)"></span>
 </label>
 <span class="active-parameter-label" *ngIf="isActive && activeItem">{{activeItem.name | translate}}:</span>
 <div [ngClass]="{'search-item-container': true, 'active': isActive, 'value': isValueInput}">
index 6921b53..973db61 100644 (file)
@@ -20,7 +20,7 @@
 </button>
 <div class="dropdown-menu row col-md-12">
   <div class="col-md-4" (click)="$event.stopPropagation()">
-    <h4>{{'filters.timeRange' | translate}}</h4>
+    <h4>{{'filter.timeRange' | translate}}</h4>
     <date-picker (timeChange)="setStartTime($event)"></date-picker>
     <date-picker (timeChange)="setEndTime($event)"></date-picker>
     <button class="btn btn-success pull-right" type="button" (click)="setCustomTimeRange()"
@@ -29,7 +29,7 @@
     </button>
   </div>
   <div class="col-md-8 row">
-    <h4>{{'filters.timeRange.quick' | translate}}</h4>
+    <h4>{{'filter.timeRange.quick' | translate}}</h4>
     <div *ngFor="let group of quickRanges" [ngClass]="'col-md-' + 12 / quickRanges.length">
       <div *ngFor="let option of group">
         <span class="time-range-name" (click)="setTimeRange(option.value, option.label)">
index fc740de..fc0f6ae 100644 (file)
@@ -18,6 +18,7 @@
 
 import {Component, OnInit, Input, forwardRef} from '@angular/core';
 import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {Moment} from 'moment';
 import {FilteringService} from '@app/services/filtering.service';
 
 @Component({
@@ -46,9 +47,9 @@ export class TimeRangePickerComponent implements OnInit, ControlValueAccessor {
 
   selectedLabel: string;
 
-  startTime: number;
+  startTime: Moment;
 
-  endTime: number;
+  endTime: Moment;
 
   private onChange: (fn: any) => void;
 
@@ -67,12 +68,12 @@ export class TimeRangePickerComponent implements OnInit, ControlValueAccessor {
     this.onChange(newValue);
   }
 
-  setStartTime(timeStamp: number): void {
-    this.startTime = timeStamp;
+  setStartTime(timeObject: Moment): void {
+    this.startTime = timeObject;
   }
 
-  setEndTime(timeStamp: number): void {
-    this.endTime = timeStamp;
+  setEndTime(timeObject: Moment): void {
+    this.endTime = timeObject;
   }
 
   setTimeRange(value: any, label: string) {
index abbc9ce..7d1e907 100644 (file)
@@ -24,9 +24,15 @@ 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 {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 {ComponentActionsService} from '@app/services/component-actions.service';
 import {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
 import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe';
 import {ModalComponent} from '@app/components/modal/modal.component';
 
@@ -49,7 +55,12 @@ describe('TimeZonePickerComponent', () => {
           appState,
           clusters,
           components,
-          hosts
+          hosts,
+          auditLogs,
+          auditLogsFields,
+          serviceLogs,
+          serviceLogsFields,
+          serviceLogsHistogramData
         }),
         ...TranslationModules
       ],
@@ -59,9 +70,15 @@ describe('TimeZonePickerComponent', () => {
         ClustersService,
         ComponentsService,
         HostsService,
+        AuditLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
         ComponentActionsService,
         FilteringService,
-        HttpClientService
+        HttpClientService,
+        LogsContainerService
       ],
     })
     .compileComponents();
index 6898354..3af1a69 100644 (file)
@@ -15,7 +15,7 @@
   limitations under the License.
 -->
 
-<menu-button *ngFor="let item of items" [label]="item.label" [action]="item.action"
+<menu-button *ngFor="let item of items" label="{{item.label | translate}}" [action]="item.action"
              [iconClass]="item.iconClass" [labelClass]="item.labelClass"
              [subItems]="item.subItems" [hideCaret]="item.hideCaret">
 </menu-button>
index 1649a50..6679ba1 100644 (file)
@@ -18,6 +18,7 @@
 
 import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
 import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+import {TranslationModules} from '@app/test-config.spec';
 
 import {TopMenuComponent} from './top-menu.component';
 
@@ -27,6 +28,7 @@ describe('TopMenuComponent', () => {
 
   beforeEach(async(() => {
     TestBed.configureTestingModule({
+      imports: TranslationModules,
       declarations: [TopMenuComponent],
       schemas: [CUSTOM_ELEMENTS_SCHEMA]
     })
index ab95030..88b0c91 100644 (file)
@@ -34,7 +34,7 @@
 @filters-panel-padding: 10px 0;
 @list-header-background-color: #F2F2F2;
 @checkbox-top: 4px;
-@dropdown-min-width: 200px;
+@dropdown-min-width: 160px;
 @dropdown-max-height: 500px; // TODO get rid of magic number, base on actual design
 @input-height: 34px;
 @input-padding: 10px;
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/pipes/timer-seconds.pipe.spec.ts b/ambari-logsearch/ambari-logsearch-web/src/app/pipes/timer-seconds.pipe.spec.ts
new file mode 100644 (file)
index 0000000..a81e0cc
--- /dev/null
@@ -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 {TestBed, inject} from '@angular/core/testing';
+import {UtilsService} from '@app/services/utils.service';
+
+import {TimerSecondsPipe} from './timer-seconds.pipe';
+
+describe('TimerSecondsPipe', () => {
+  beforeEach(() => {
+    TestBed.configureTestingModule({
+      providers: [
+        UtilsService
+      ]
+    });
+  });
+
+  it('create an instance', inject([UtilsService], (utils: UtilsService) => {
+    const pipe = new TimerSecondsPipe(utils);
+    expect(pipe).toBeTruthy();
+  }));
+});
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/pipes/timer-seconds.pipe.ts b/ambari-logsearch/ambari-logsearch-web/src/app/pipes/timer-seconds.pipe.ts
new file mode 100644 (file)
index 0000000..1b9dc76
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * 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 {Pipe, PipeTransform} from '@angular/core';
+import {UtilsService} from '@app/services/utils.service';
+
+@Pipe({
+  name: 'timerSeconds'
+})
+export class TimerSecondsPipe implements PipeTransform {
+
+  constructor(private utils: UtilsService) {
+  }
+
+  transform(value: number): string {
+    const seconds = value % 60,
+      outputSeconds = this.utils.fitIntegerDigitsCount(seconds),
+      fullMinutes = (value - seconds) / 60,
+      minutes = fullMinutes % 60,
+      outputMinutes = this.utils.fitIntegerDigitsCount(minutes),
+      hours = (fullMinutes - minutes) / 60,
+      outputHours = hours ? `${this.utils.fitIntegerDigitsCount(hours)}:` : '';
+    return `${outputHours}${outputMinutes}:${outputSeconds}`;
+  }
+
+}
index f961907..e737155 100644 (file)
@@ -22,8 +22,14 @@ import {AppSettingsService, appSettings} from '@app/services/storage/app-setting
 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 {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 {FilteringService} from '@app/services/filtering.service';
 import {HttpClientService} from '@app/services/http-client.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
 
 import {ComponentActionsService} from './component-actions.service';
 
@@ -44,7 +50,12 @@ describe('ComponentActionsService', () => {
           appSettings,
           clusters,
           components,
-          hosts
+          hosts,
+          auditLogs,
+          serviceLogs,
+          auditLogsFields,
+          serviceLogsFields,
+          serviceLogsHistogramData
         })
       ],
       providers: [
@@ -53,11 +64,17 @@ describe('ComponentActionsService', () => {
         ClustersService,
         ComponentsService,
         HostsService,
+        AuditLogsService,
+        ServiceLogsService,
+        AuditLogsFieldsService,
+        ServiceLogsFieldsService,
+        ServiceLogsHistogramDataService,
         FilteringService,
         {
           provide: HttpClientService,
           useValue: httpClient
-        }
+        },
+        LogsContainerService
       ]
     });
   });
index f29705c..dba0f8f 100644 (file)
@@ -20,11 +20,12 @@ import {Injectable} from '@angular/core';
 import {AppSettingsService} from '@app/services/storage/app-settings.service';
 import {CollectionModelService} from '@app/models/store.model';
 import {FilteringService} from '@app/services/filtering.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
 
 @Injectable()
 export class ComponentActionsService {
 
-  constructor(private appSettings: AppSettingsService, private filtering: FilteringService) {
+  constructor(private appSettings: AppSettingsService, private filtering: FilteringService, private logsContainer: LogsContainerService) {
   }
 
   //TODO implement actions
@@ -35,12 +36,22 @@ export class ComponentActionsService {
   redo() {
   }
 
-  refresh() {
+  refresh(): void {
+    // TODO implement dynamic definition of logs type
+    this.logsContainer.loadLogs('serviceLogs');
   }
 
   openHistory() {
   }
 
+  startCapture(): void {
+    this.filtering.startCaptureTimer();
+  }
+
+  stopCapture(): void {
+    this.filtering.stopCaptureTimer();
+  }
+
   setTimeZone(timeZone: string): void {
     this.appSettings.setParameter('timeZone', timeZone);
   }
index beaf91c..6697c54 100644 (file)
 import {Injectable} from '@angular/core';
 import {FormControl, FormGroup} from '@angular/forms';
 import {Subject} from 'rxjs/Subject';
+import {Observable} from 'rxjs/Observable';
+import 'rxjs/add/observable/timer';
+import 'rxjs/add/operator/takeUntil';
+import {Moment} from 'moment';
 import * as moment from 'moment-timezone';
 import {ListItem} from '@app/classes/list-item.class';
 import {AppSettingsService} from '@app/services/storage/app-settings.service';
@@ -190,16 +194,6 @@ export class FilteringService {
               unit: 'd'
             }
           },
-          // TODO implement time range calculation
-          /*
-          {
-            label: 'filter.timeRange.todaySoFar',
-            value: {
-              type: 'CURRENT',
-              unit: 'd'
-            }
-          },
-          */
           {
             label: 'filter.timeRange.thisWeek',
             value: {
@@ -207,16 +201,6 @@ export class FilteringService {
               unit: 'w'
             }
           },
-          // TODO implement time range calculation
-          /*
-          {
-            label: 'filter.timeRange.thisWeekSoFar',
-            value: {
-              type: 'CURRENT',
-              unit: 'w'
-            }
-          },
-          */
           {
             label: 'filter.timeRange.thisMonth',
             value: {
@@ -406,6 +390,43 @@ export class FilteringService {
 
   queryParameterAdd: Subject<any> = new Subject();
 
+  private stopTimer: Subject<any> = new Subject();
+
+  private stopAutoRefreshCountdown: Subject<any> = new Subject();
+
+  captureSeconds: number = 0;
+
+  private readonly autoRefreshInterval: number = 30000;
+
+  autoRefreshRemainingSeconds: number = 0;
+
+  private startCaptureMoment: Moment;
+
+  private stopCaptureMoment: Moment;
+
+  startCaptureTimer(): void {
+    this.startCaptureMoment = moment();
+    Observable.timer(0, 1000).takeUntil(this.stopTimer).subscribe(seconds => this.captureSeconds = seconds);
+  }
+
+  stopCaptureTimer(): void {
+    const autoRefreshIntervalSeconds = this.autoRefreshInterval / 1000;
+    this.stopCaptureMoment = moment();
+    this.captureSeconds = 0;
+    this.stopTimer.next();
+    Observable.timer(0, 1000).takeUntil(this.stopAutoRefreshCountdown).subscribe(seconds => {
+      this.autoRefreshRemainingSeconds = autoRefreshIntervalSeconds - seconds;
+      if (!this.autoRefreshRemainingSeconds) {
+        this.stopAutoRefreshCountdown.next();
+        this.filtersForm.controls.timeRange.setValue({
+          type: 'CUSTOM',
+          start: this.startCaptureMoment,
+          end: this.stopCaptureMoment
+        });
+      }
+    });
+  }
+
   loadClusters(): void {
     this.httpClient.get('clusters').subscribe(response => {
       const clusterNames = response.json();
@@ -498,8 +519,6 @@ export class FilteringService {
   }
 
   readonly valueGetters = {
-    end_time: this.getEndTime,
-    start_time: this.getStartTime,
     to: this.getEndTime,
     from: this.getStartTime,
     sortType: value => value && value.type,
index 26d1b6b..bef28cf 100644 (file)
@@ -43,7 +43,7 @@ export class LogsContainerService {
 
   private readonly listFilters = {
     clusters: ['clusters'],
-    timeRange: ['end_time', 'start_time'],
+    timeRange: ['to', 'from'],
     components: ['mustBe'],
     levels: ['level'],
     hosts: ['hostList'],
@@ -111,9 +111,7 @@ export class LogsContainerService {
         let value;
         const valueGetter = this.filtering.valueGetters[paramName];
         if (valueGetter) {
-          if (paramName === 'start_time') {
-            value = valueGetter(inputValue, params['end_time']);
-          } else if (paramName === 'from') {
+          if (paramName === 'from') {
             value = valueGetter(inputValue, params['to']);
           } else {
             value = valueGetter(inputValue);
index 937fae1..8b157df 100644 (file)
@@ -52,11 +52,11 @@ export class mockApiDataService implements InMemoryDbService {
           key: 'log_message',
           filterFunction: (value, filterValue) => value.toLowerCase().indexOf(filterValue.toLowerCase()) > -1
         },
-        start_time: {
+        from: {
           key: 'logtime',
           filterFunction: (value, filterValue) => value >= moment(filterValue).valueOf()
         },
-        end_time: {
+        to: {
           key: 'logtime',
           filterFunction: (value, filterValue) => value < moment(filterValue).valueOf()
         },
index 773172b..3448dd4 100644 (file)
@@ -54,4 +54,10 @@ export class UtilsService {
     return !momentA.isSame(momentB, 'day');
   }
 
+  fitIntegerDigitsCount(numberToFormat: number, minLength: number = 2): string {
+    return numberToFormat.toLocaleString(undefined, {
+      minimumIntegerDigits: minLength
+    });
+  }
+
 }
index 8750d64..0d40bc7 100644 (file)
   "filter.clusters": "Clusters",
   "filter.components": "Components",
   "filter.levels": "Levels",
-  "filter.capture": "Capture",
   "filter.excluded": "Excluded",
   "filter.hosts": "Hosts",
 
-  "filters.timeRange": "Time Range",
-  "filters.timeRange.from": "from",
-  "filters.timeRange.to": "to",
-  "filters.timeRange.quick": "Quick Ranges",
+  "filter.capture": "Capture",
+  "filter.capture.triggeringRefresh": "Triggering auto-refresh in {{remainingSeconds}} sec",
+
+  "filter.timeRange": "Time Range",
+  "filter.timeRange.quick": "Quick Ranges",
   "filter.timeRange.7d": "Last 7 days",
   "filter.timeRange.30d": "Last 30 days",
   "filter.timeRange.60d": "Last 60 days",