This is an automated email from the ASF dual-hosted git repository.
ababiichuk pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push:
new 3869055 AMBARI-22900 Log Search UI: implement 'History' functionality
3869055 is described below
commit 3869055ed2cf643271b103fe85c161940c7196a0
Author: aBabiichuk <[email protected]>
AuthorDate: Fri Feb 2 14:21:16 2018 +0200
AMBARI-22900 Log Search UI: implement 'History' functionality
---
.../ambari-logsearch-web/src/app/app.module.ts | 13 +-
.../classes/components/graph/graph.component.ts | 18 +-
.../src/app/classes/filtering.ts | 2 +-
.../src/app/classes/list-item.ts | 5 +-
.../src/app/classes/models/app-state.ts | 13 +-
.../action-menu/action-menu.component.html | 15 +-
.../action-menu/action-menu.component.spec.ts | 69 ++++-
.../action-menu/action-menu.component.ts | 109 +++----
.../audit-logs-entries.component.html | 4 +-
.../audit-logs-entries.component.spec.ts | 2 +
.../audit-logs-table.component.html | 7 +-
.../audit-logs-table/audit-logs-table.component.ts | 8 +-
.../context-menu/context-menu.component.spec.ts | 6 +-
.../dropdown-button/dropdown-button.component.html | 20 +-
.../dropdown-button/dropdown-button.component.less | 8 +-
.../dropdown-button.component.spec.ts | 12 +-
.../dropdown-button/dropdown-button.component.ts | 40 +--
.../dropdown-list/dropdown-list.component.html | 28 +-
.../dropdown-list/dropdown-list.component.spec.ts | 6 +-
.../dropdown-list/dropdown-list.component.ts | 39 ++-
.../filter-button/filter-button.component.spec.ts | 12 +-
.../filter-dropdown.component.spec.ts | 12 +-
.../filter-dropdown/filter-dropdown.component.ts | 5 -
.../filters-panel/filters-panel.component.html | 10 +-
.../filters-panel/filters-panel.component.ts | 17 ++
.../history-item-controls.component.html} | 8 +-
.../history-item-controls.component.less} | 9 +-
.../history-item-controls.component.spec.ts} | 16 +-
.../history-item-controls.component.ts} | 16 +-
.../log-context/log-context.component.spec.ts | 4 +-
.../menu-button/menu-button.component.html | 8 +-
.../menu-button/menu-button.component.spec.ts | 12 +-
.../menu-button/menu-button.component.ts | 28 +-
.../pagination/pagination.component.html | 3 +-
.../components/search-box/search-box.component.ts | 4 +-
.../service-logs-table.component.html | 13 +-
.../service-logs-table.component.spec.ts | 2 -
.../service-logs-table.component.ts | 99 +++++--
.../time-range-picker.component.spec.ts | 2 +
.../time-range-picker.component.ts | 2 +-
.../timezone-picker.component.spec.ts | 2 -
.../timezone-picker/timezone-picker.component.ts | 17 +-
.../components/top-menu/top-menu.component.html | 8 +-
.../components/top-menu/top-menu.component.spec.ts | 4 +
.../app/components/top-menu/top-menu.component.ts | 23 +-
.../app/services/component-actions.service.spec.ts | 101 -------
.../src/app/services/component-actions.service.ts | 155 ----------
.../services/component-generator.service.spec.ts | 2 +
.../app/services/component-generator.service.ts | 6 +
...ice.spec.ts => history-manager.service.spec.ts} | 94 +++++-
.../src/app/services/history-manager.service.ts | 330 +++++++++++++++++++++
.../app/services/logs-container.service.spec.ts | 4 +-
.../src/app/services/logs-container.service.ts | 88 +++++-
.../ambari-logsearch-web/src/assets/i18n/en.json | 5 +
54 files changed, 944 insertions(+), 601 deletions(-)
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
index c6c6922..0a42994 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/app.module.ts
@@ -35,7 +35,6 @@ import {ServiceInjector} from '@app/classes/service-injector';
import {mockApiDataService} from '@app/services/mock-api-data.service'
import {HttpClientService} from '@app/services/http-client.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {UtilsService} from '@app/services/utils.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {ComponentGeneratorService} from
'@app/services/component-generator.service';
@@ -57,6 +56,7 @@ import {ServiceLogsFieldsService} from
'@app/services/storage/service-logs-field
import {AuditLogsFieldsService} from
'@app/services/storage/audit-logs-fields.service';
import {TabsService} from '@app/services/storage/tabs.service';
import {AuthService} from '@app/services/auth.service';
+import {HistoryManagerService} from '@app/services/history-manager.service';
import {reducer} from '@app/services/storage/reducers.service';
import {AppComponent} from '@app/components/app.component';
@@ -96,6 +96,7 @@ import {GraphTooltipComponent} from
'@app/components/graph-tooltip/graph-tooltip
import {GraphLegendItemComponent} from
'@app/components/graph-legend-item/graph-legend-item.component';
import {TimeLineGraphComponent} from
'@app/components/time-line-graph/time-line-graph.component';
import {ContextMenuComponent} from
'@app/components/context-menu/context-menu.component';
+import {HistoryItemControlsComponent} from
'@app/components/history-item-controls/history-item-controls.component';
import {TimeZoneAbbrPipe} from '@app/pipes/timezone-abbr.pipe';
import {TimerSecondsPipe} from '@app/pipes/timer-seconds.pipe';
@@ -159,6 +160,7 @@ export function getXHRBackend(injector: Injector, browser:
BrowserXhr, xsrf: XSR
GraphLegendItemComponent,
TimeLineGraphComponent,
ContextMenuComponent,
+ HistoryItemControlsComponent,
TimeZoneAbbrPipe,
TimerSecondsPipe
],
@@ -183,7 +185,6 @@ export function getXHRBackend(injector: Injector, browser:
BrowserXhr, xsrf: XSR
],
providers: [
HttpClientService,
- ComponentActionsService,
UtilsService,
LogsContainerService,
ComponentGeneratorService,
@@ -208,10 +209,14 @@ export function getXHRBackend(injector: Injector,
browser: BrowserXhr, xsrf: XSR
useFactory: getXHRBackend,
deps: [Injector, BrowserXhr, XSRFStrategy, ResponseOptions]
},
- AuthService
+ AuthService,
+ HistoryManagerService
],
bootstrap: [AppComponent],
- entryComponents: [NodeBarComponent],
+ entryComponents: [
+ NodeBarComponent,
+ HistoryItemControlsComponent
+ ],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
index 0cfe69a..b9140cd 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/components/graph/graph.component.ts
@@ -122,20 +122,6 @@ export class GraphComponent implements AfterViewInit,
OnChanges {
skipZeroValuesInTooltip: boolean = true;
/**
- * Indicates whether context menu for X axis ticks is available
- * @type {boolean}
- */
- @Input()
- hasXTickContextMenu: boolean = false;
-
- /**
- * Indicates whether context menu for Y axis ticks is available
- * @type {boolean}
- */
- @Input()
- hasYTickContextMenu: boolean = false;
-
- /**
* Indicates whether X axis event should be emitted with formatted string
values that are displayed
* (instead of raw values)
* @type {boolean}
@@ -320,7 +306,7 @@ export class GraphComponent implements AfterViewInit,
OnChanges {
this.xAxis = axis;
this.svg.append('g').attr('class', `axis
${this.xAxisClassName}`).attr('transform', `translate(0,${this.height})`)
.call(this.xAxis);
- if (this.hasXTickContextMenu) {
+ if (this.xTickContextMenu.observers.length) {
this.svg.selectAll(`.${this.xAxisClassName} .tick`).on('contextmenu',
(tickValue: any, index: number): void => {
const tick = this.emitFormattedXTick ?
this.xAxisTickFormatter(tickValue, index) : tickValue,
nativeEvent = d3.event;
@@ -342,7 +328,7 @@ export class GraphComponent implements AfterViewInit,
OnChanges {
}
this.yAxis = axis;
this.svg.append('g').attr('class', `axis
${this.yAxisClassName}`).call(this.yAxis);
- if (this.hasYTickContextMenu) {
+ if (this.yTickContextMenu.observers.length) {
this.svg.selectAll(`.${this.yAxisClassName} .tick`).on('contextmenu',
(tickValue: any, index: number): void => {
const tick = this.emitFormattedYTick ?
this.yAxisTickFormatter(tickValue, index): tickValue,
nativeEvent = d3.event;
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
index 3348969..bb75786 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/filtering.ts
@@ -48,7 +48,7 @@ export interface SortingListItem extends ListItem {
export interface FilterCondition {
label?: string;
options?: (ListItem | TimeUnitListItem[])[];
- defaultSelection?: ListItem | ListItem[] | number;
+ defaultSelection?: ListItem | ListItem[] | number | boolean;
iconClass?: string;
fieldName?: string;
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
index 1aaaecc..8505373 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
@@ -17,10 +17,11 @@
*/
export interface ListItem {
- id?: string;
+ id?: string | number;
label?: string;
value: any;
iconClass?: string;
isChecked?: boolean;
- action?: string;
+ onSelect?: Function;
+ isDivider?: boolean;
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
index c3279ce..b187ca6 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
+++ b/ambari-logsearch/ambari-logsearch-web/src/app/classes/models/app-state.ts
@@ -17,8 +17,14 @@
*/
import {ActiveServiceLogEntry} from '@app/classes/active-service-log-entry';
+import {ListItem} from '@app/classes/list-item';
import {LogsType} from '@app/classes/string';
+export interface History {
+ items: ListItem[];
+ currentId: number;
+}
+
export interface AppState {
isAuthorized: boolean;
isInitialLoading: boolean;
@@ -28,6 +34,7 @@ export interface AppState {
isServiceLogContextView: boolean;
activeLog: ActiveServiceLogEntry | null;
activeFilters: object;
+ history: History;
}
export const initialState: AppState = {
@@ -38,5 +45,9 @@ export const initialState: AppState = {
isServiceLogsFileView: false,
isServiceLogContextView: false,
activeLog: null,
- activeFilters: null
+ activeFilters: null,
+ history: {
+ items: [],
+ currentId: -1
+ }
};
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html
index ab6326a..2e332d4 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html
@@ -14,7 +14,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<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" [badge]="item.badge"
[isRightAlign]="item.isRightAlign">
-</menu-button>
+
+<!-- TODO use listClass="history-dropdown" for custom styling -->
+<menu-button label="{{'topMenu.undo' | translate}}" [subItems]="undoItems"
iconClass="fa fa-arrow-left"
+ listClass="history-dropdown" (buttonClick)="undoLatest()"
(selectItem)="undo($event)"></menu-button>
+<menu-button label="{{'topMenu.redo' | translate}}" [subItems]="redoItems"
iconClass="fa fa-arrow-right"
+ listClass="history-dropdown" (buttonClick)="redoLatest()"
(selectItem)="redo($event)">></menu-button>
+<menu-button label="{{'topMenu.history' | translate}}"
[subItems]="historyItems" iconClass="fa fa-history"
+ listClass="history-dropdown" [isRightAlign]="true"
+
additionalLabelComponentSetter="getHistoryItemIcons"></menu-button>
+<menu-button label="{{'topMenu.refresh' | translate}}" iconClass="fa
fa-refresh"
+ (buttonClick)="refresh()"></menu-button>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts
index 081304e..ba53ee1 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts
@@ -19,6 +19,26 @@
import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TranslationModules} from '@app/test-config.spec';
+import {StoreModule} from '@ngrx/store';
+import {AuditLogsService, auditLogs} from
'@app/services/storage/audit-logs.service';
+import {ServiceLogsService, serviceLogs} from
'@app/services/storage/service-logs.service';
+import {AuditLogsFieldsService, auditLogsFields} from
'@app/services/storage/audit-logs-fields.service';
+import {AuditLogsGraphDataService, auditLogsGraphData} from
'@app/services/storage/audit-logs-graph-data.service';
+import {ServiceLogsFieldsService, serviceLogsFields} from
'@app/services/storage/service-logs-fields.service';
+import {
+ ServiceLogsHistogramDataService, serviceLogsHistogramData
+} from '@app/services/storage/service-logs-histogram-data.service';
+import {AppSettingsService, appSettings} from
'@app/services/storage/app-settings.service';
+import {AppStateService, appState} from
'@app/services/storage/app-state.service';
+import {ClustersService, clusters} from
'@app/services/storage/clusters.service';
+import {ComponentsService, components} from
'@app/services/storage/components.service';
+import {HostsService, hosts} from '@app/services/storage/hosts.service';
+import {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
+import {TabsService, tabs} from '@app/services/storage/tabs.service';
+import {HistoryManagerService} from '@app/services/history-manager.service';
+import {HttpClientService} from '@app/services/http-client.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {UtilsService} from '@app/services/utils.service';
import {ActionMenuComponent} from './action-menu.component';
@@ -27,9 +47,56 @@ describe('ActionMenuComponent', () => {
let fixture: ComponentFixture<ActionMenuComponent>;
beforeEach(async(() => {
+ const httpClient = {
+ get: () => {
+ return {
+ subscribe: () => {
+ }
+ }
+ }
+ };
TestBed.configureTestingModule({
- imports: TranslationModules,
+ imports: [
+ ...TranslationModules,
+ StoreModule.provideStore({
+ auditLogs,
+ serviceLogs,
+ auditLogsFields,
+ auditLogsGraphData,
+ serviceLogsFields,
+ serviceLogsHistogramData,
+ appSettings,
+ appState,
+ clusters,
+ components,
+ hosts,
+ serviceLogsTruncated,
+ tabs
+ })
+ ],
declarations: [ActionMenuComponent],
+ providers: [
+ {
+ provide: HttpClientService,
+ useValue: httpClient
+ },
+ HistoryManagerService,
+ LogsContainerService,
+ UtilsService,
+ AuditLogsService,
+ ServiceLogsService,
+ AuditLogsFieldsService,
+ AuditLogsGraphDataService,
+ ServiceLogsFieldsService,
+ ServiceLogsHistogramDataService,
+ AppSettingsService,
+ AppStateService,
+ ClustersService,
+ ComponentsService,
+ HostsService,
+ ServiceLogsTruncatedService,
+ TabsService
+ ],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
.compileComponents();
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
index 72037f8..351268c 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.ts
@@ -17,6 +17,9 @@
*/
import {Component} from '@angular/core';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {HistoryManagerService} from '@app/services/history-manager.service';
+import {ListItem} from '@app/classes/list-item';
@Component({
selector: 'action-menu',
@@ -25,77 +28,39 @@ import {Component} from '@angular/core';
})
export class ActionMenuComponent {
- //TODO implement loading of real data into subItems
- readonly items = [
- {
- iconClass: 'fa fa-arrow-left',
- label: 'topMenu.undo',
- action: 'undo',
- subItems: [
- {
- label: 'Apply \'Last week\' filter'
- },
- {
- label: 'Clear all filters'
- },
- {
- label: 'Apply \'HDFS\' filter'
- },
- {
- label: 'Apply \'Errors\' filter'
- }
- ]
- },
- {
- iconClass: 'fa fa-arrow-right',
- label: 'topMenu.redo',
- action: 'redo',
- subItems: [
- {
- label: 'Apply \'Warnings\' filter'
- },
- {
- label: 'Switch to graph mode'
- },
- {
- label: 'Apply \'Custom Date\' filter'
- }
- ]
- },
- {
- iconClass: 'fa fa-refresh',
- label: 'topMenu.refresh',
- action: 'refresh'
- },
- {
- iconClass: 'fa fa-history',
- label: 'topMenu.history',
- action: 'openHistory',
- isRightAlign: true,
- subItems: [
- {
- label: 'Apply \'Custom Date\' filter'
- },
- {
- label: 'Switch to graph mode'
- },
- {
- label: 'Apply \'Warnings\' filter'
- },
- {
- label: 'Apply \'Last week\' filter'
- },
- {
- label: 'Clear all filters'
- },
- {
- label: 'Apply \'HDFS\' filter'
- },
- {
- label: 'Apply \'Errors\' filter'
- }
- ]
- }
- ];
+ constructor(private logsContainer: LogsContainerService, private
historyManager: HistoryManagerService) {
+ }
+
+ get undoItems(): ListItem[] {
+ return this.historyManager.undoItems;
+ }
+
+ get redoItems(): ListItem[] {
+ return this.historyManager.redoItems;
+ }
+
+ get historyItems(): ListItem[] {
+ return this.historyManager.activeHistory;
+ }
+
+ undoLatest(): void {
+ this.historyManager.undo(this.undoItems[0]);
+ }
+
+ redoLatest(): void {
+ this.historyManager.redo(this.redoItems[0]);
+ }
+
+ undo(item: ListItem): void {
+ this.historyManager.undo(item);
+ }
+
+ redo(item: ListItem): void {
+ this.historyManager.redo(item);
+ }
+
+ refresh(): void {
+ this.logsContainer.loadLogs();
+ }
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
index 22deef1..6ebb92e 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.html
@@ -25,8 +25,8 @@
svgId="top-users-graph"></horizontal-histogram>
</collapsible-panel>
<collapsible-panel commonTitle="{{'logs.topResources' | translate:
resourcesGraphTitleParams}}" class="col-md-6">
- <horizontal-histogram [data]="topResourcesGraphData"
[allowFractionalXTicks]="false" [hasYTickContextMenu]="true"
- [emitFormattedYTick]="true"
(yTickContextMenu)="showContextMenu($event)"
+ <horizontal-histogram [data]="topResourcesGraphData"
[allowFractionalXTicks]="false" [emitFormattedYTick]="true"
+ (yTickContextMenu)="showContextMenu($event)"
svgId="top-resources-graph"></horizontal-histogram>
<context-menu [isDisplayed]="isContextMenuDisplayed"
[contextMenuItems]="contextMenuItems"
[leftPosition]="contextMenuLeft"
[topPosition]="contextMenuTop" (itemSelect)="updateQuery($event)"
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
index f7d0cdc..51aaaa1 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-entries/audit-logs-entries.component.spec.ts
@@ -37,6 +37,7 @@ import {TabsService, tabs} from
'@app/services/storage/tabs.service';
import {TabsComponent} from '@app/components/tabs/tabs.component';
import {LogsContainerService} from '@app/services/logs-container.service';
import {HttpClientService} from '@app/services/http-client.service';
+import {UtilsService} from '@app/services/utils.service';
import {AuditLogsEntriesComponent} from './audit-logs-entries.component';
@@ -82,6 +83,7 @@ describe('AuditLogsEntriesComponent', () => {
provide: HttpClientService,
useValue: httpClient
},
+ UtilsService,
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.html
index cad09bc..f970726 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.html
@@ -15,13 +15,12 @@
limitations under the License.
-->
-<dropdown-button class="pull-right" label="{{'logs.columns' | translate}}"
[options]="columns" [isRightAlign]="true"
- [isMultipleChoice]="true" action="updateSelectedColumns"
-
[additionalArgs]="logsTypeMapObject.fieldsModel"></dropdown-button>
+<dropdown-button class="pull-right" label="{{'logs.columns' | translate}}"
(selectItem)="updateSelectedColumns($event)"
+ [options]="columns" [isRightAlign]="true"
[isMultipleChoice]="true"></dropdown-button>
<form *ngIf="logs && logs.length" [formGroup]="filtersForm" class="row
pull-right">
<filter-dropdown class="col-md-12" label="{{filters.auditLogsSorting.label |
translate}}"
formControlName="auditLogsSorting"
[options]="filters.auditLogsSorting.options"
- [isRightAlign]="true"></filter-dropdown>
+ [isRightAlign]="true"
[showCommonLabelWithSelection]="true"></filter-dropdown>
</form>
<div class="panel panel-default">
<div class="panel-body">
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
index deca936..fa5b1c5 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/audit-logs-table/audit-logs-table.component.ts
@@ -36,9 +36,7 @@ export class AuditLogsTableComponent extends
LogsTableComponent {
readonly timeFormat: string = 'YYYY-MM-DD HH:mm:ss,SSS';
- get logsTypeMapObject(): object {
- return this.logsContainer.logsTypeMap.auditLogs;
- }
+ private readonly logsType: string = 'auditLogs';
get filters(): any {
return this.logsContainer.filters;
@@ -52,4 +50,8 @@ export class AuditLogsTableComponent extends
LogsTableComponent {
return this.columns.find((column: ListItem): boolean => column.value ===
name);
}
+ updateSelectedColumns(columns: string[]): void {
+ this.logsContainer.updateSelectedColumns(columns, this.logsType);
+ }
+
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/context-menu/context-menu.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/context-menu/context-menu.component.spec.ts
index 1c5154b..1881f57 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/context-menu/context-menu.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/context-menu/context-menu.component.spec.ts
@@ -39,8 +39,8 @@ import {TabsService, tabs} from
'@app/services/storage/tabs.service';
import {ComponentGeneratorService} from
'@app/services/component-generator.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {HttpClientService} from '@app/services/http-client.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {AuthService} from '@app/services/auth.service';
+import {UtilsService} from '@app/services/utils.service';
import {DropdownListComponent} from
'@app/components/dropdown-list/dropdown-list.component';
import {ContextMenuComponent} from './context-menu.component';
@@ -90,7 +90,6 @@ describe('ContextMenuComponent', () => {
provide: HttpClientService,
useValue: httpClient
},
- ComponentActionsService,
HostsService,
AuditLogsService,
ServiceLogsService,
@@ -104,7 +103,8 @@ describe('ContextMenuComponent', () => {
ComponentsService,
ServiceLogsTruncatedService,
TabsService,
- AuthService
+ AuthService,
+ UtilsService
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
index d047d7a..396a277 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.html
@@ -16,16 +16,20 @@
-->
<div [ngClass]="{'dropup': isDropup}">
- <button [ngClass]="['btn', 'btn-link', 'dropdown-toggle', buttonClass]"
data-toggle="dropdown">
- <span *ngIf="iconClass || label"
- [ngClass]="{'filter-label': true, 'plain': !isMultipleChoice &&
!hideCaret && showSelectedValue}">
- <span *ngIf="iconClass" [ngClass]="iconClass"></span>
- <span *ngIf="label">{{label}}</span>
+ <button [ngClass]="['btn', 'dropdown-toggle', buttonClass]"
data-toggle="dropdown">
+ <span class="filter-label">
+ <span *ngIf="iconClass || label" [class.plain]="!isMultipleChoice &&
!hideCaret && showSelectedValue">
+ <span *ngIf="iconClass" [ngClass]="iconClass"></span>
+ <span *ngIf="label && (!selection.length || isMultipleChoice ||
showCommonLabelWithSelection)"
+ [class.label-before-selection]="isSelectionDisplayable">
+ {{label}}
+ </span>
+ </span>
+ <span *ngIf="isSelectionDisplayable">{{selection[0].label |
translate}}</span>
+ <span *ngIf="!hideCaret" class="caret"></span>
</span>
- <span *ngIf="showSelectedValue && !isMultipleChoice &&
selection.length">{{selection[0].label | translate}}</span>
- <span *ngIf="!hideCaret" class="caret"></span>
</button>
<ul data-component="dropdown-list" [ngClass]="{'dropdown-menu': true,
'dropdown-menu-right': isRightAlign}"
[items]="options" [isMultipleChoice]="isMultipleChoice"
(selectedItemChange)="updateSelection($event)"
- [actionArguments]="additionalArgs"></ul>
+ [actionArguments]="listItemArguments"></ul>
</div>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.less
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.less
index 7b560c1..06861722 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.less
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.less
@@ -25,11 +25,13 @@
text-transform: none;
.filter-label {
- padding: @input-group-addon-padding;
-
- &.plain {
+ .plain {
color: initial;
}
+
+ .label-before-selection {
+ padding: @input-group-addon-padding;
+ }
}
}
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
index b9f9540..8a72c38 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.spec.ts
@@ -16,11 +16,10 @@
* limitations under the License.
*/
-import {NO_ERRORS_SCHEMA, Injector} from '@angular/core';
-import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TranslationModules} from '@app/test-config.spec';
import {StoreModule} from '@ngrx/store';
-import {ServiceInjector} from '@app/classes/service-injector';
import {AppSettingsService, appSettings} from
'@app/services/storage/app-settings.service';
import {ClustersService, clusters} from
'@app/services/storage/clusters.service';
import {ComponentsService, components} from
'@app/services/storage/components.service';
@@ -37,7 +36,6 @@ import {
import {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.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 {AuthService} from '@app/services/auth.service';
@@ -92,7 +90,6 @@ describe('DropdownButtonComponent', () => {
ServiceLogsTruncatedService,
TabsService,
UtilsService,
- ComponentActionsService,
{
provide: HttpClientService,
useValue: httpClient
@@ -105,12 +102,11 @@ describe('DropdownButtonComponent', () => {
.compileComponents();
}));
- beforeEach(inject([Injector], (injector: Injector) => {
- ServiceInjector.injector = injector;
+ beforeEach(() => {
fixture = TestBed.createComponent(DropdownButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
- }));
+ });
it('should create component', () => {
expect(component).toBeTruthy();
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
index ead9e1a..b642dd5 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-button/dropdown-button.component.ts
@@ -16,10 +16,8 @@
* limitations under the License.
*/
-import {Component, Input} from '@angular/core';
+import {Component, Input, Output, EventEmitter} from '@angular/core';
import {ListItem} from '@app/classes/list-item';
-import {ServiceInjector} from '@app/classes/service-injector';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {UtilsService} from '@app/services/utils.service';
@Component({
@@ -30,14 +28,13 @@ import {UtilsService} from '@app/services/utils.service';
export class DropdownButtonComponent {
constructor(protected utils: UtilsService) {
- this.actions = ServiceInjector.injector.get(ComponentActionsService);
}
-
+
@Input()
label?: string;
@Input()
- buttonClass: string = '';
+ buttonClass: string = 'btn-link';
@Input()
iconClass?: string;
@@ -52,10 +49,7 @@ export class DropdownButtonComponent {
options: ListItem[] = [];
@Input()
- action?: string;
-
- @Input()
- additionalArgs: any[] = [];
+ listItemArguments: any[] = [];
@Input()
isMultipleChoice: boolean = false;
@@ -66,7 +60,11 @@ export class DropdownButtonComponent {
@Input()
isDropup: boolean = false;
- private actions: ComponentActionsService;
+ @Input()
+ showCommonLabelWithSelection: boolean = false;
+
+ @Output()
+ selectItem: EventEmitter<any> = new EventEmitter();
protected selectedItems?: ListItem[] = [];
@@ -78,22 +76,28 @@ export class DropdownButtonComponent {
this.selectedItems = items;
}
+ // TODO handle case of selections with multiple items
+ /**
+ * Indicates whether selection can be displayed at the moment, i.e. it's not
empty, not multiple
+ * and set to be displayed by showSelectedValue flag
+ * @returns {boolean}
+ */
+ get isSelectionDisplayable():boolean {
+ return this.showSelectedValue && !this.isMultipleChoice &&
this.selection.length > 0;
+ }
+
updateSelection(item: ListItem): void {
- const action = this.action && this.actions[this.action];
+ const hasAction = this.selectItem.observers.length;
if (this.isMultipleChoice) {
this.options.find((option: ListItem): boolean => {
return this.utils.isEqual(option.value, item.value);
}).isChecked = item.isChecked;
const checkedItems = this.options.filter((option: ListItem): boolean =>
option.isChecked);
this.selection = checkedItems;
- if (action) {
- action(checkedItems.map((option: ListItem): any => option.value),
...this.additionalArgs);
- }
+ this.selectItem.emit(checkedItems.map((option: ListItem): any =>
option.value));
} else if (!this.utils.isEqual(this.selection[0], item)) {
this.selection = [item];
- if (action) {
- action(item.value, ...this.additionalArgs);
- }
+ this.selectItem.emit(item.value);
}
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
index 64e4b8e..9861c24 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.html
@@ -15,19 +15,21 @@
limitations under the License.
-->
-<li *ngFor="let item of items">
- <label class="list-item-label" *ngIf="isMultipleChoice">
- <input type="checkbox" [attr.id]="item.id || item.value"
[(ngModel)]="item.isChecked"
- (change)="changeSelectedItem({value: item.value, isChecked:
$event.currentTarget.checked})">
- <label [attr.for]="item.id || item.value" class="label-container">
+<li *ngFor="let item of items" [class.divider]="item.isDivider"
[attr.role]="item.isDivider ? 'separator' : null">
+ <ng-container *ngIf="!item.isDivider">
+ <label class="list-item-label" *ngIf="isMultipleChoice">
+ <input type="checkbox" [attr.id]="item.id || item.value"
[(ngModel)]="item.isChecked"
+ (change)="changeSelectedItem({value: item.value, isChecked:
$event.currentTarget.checked})">
+ <label [attr.for]="item.id || item.value" class="label-container">
+ <span *ngIf="item.iconClass" [ngClass]="item.iconClass"></span>
+ {{item.label | translate}}
+ <span #additionalComponent></span>
+ </label>
+ </label>
+ <span class="list-item-label label-container" *ngIf="!isMultipleChoice"
(click)="changeSelectedItem(item)">
<span *ngIf="item.iconClass" [ngClass]="item.iconClass"></span>
{{item.label | translate}}
- <div #additionalComponent></div>
- </label>
- </label>
- <span class="list-item-label label-container" *ngIf="!isMultipleChoice"
(click)="changeSelectedItem(item)">
- <span *ngIf="item.iconClass" [ngClass]="item.iconClass"></span>
- {{item.label | translate}}
- <div #additionalComponent></div>
- </span>
+ <span #additionalComponent></span>
+ </span>
+ </ng-container>
</li>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
index dd602d7..6d88010 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.spec.ts
@@ -38,8 +38,8 @@ import {TabsService, tabs} from
'@app/services/storage/tabs.service';
import {ComponentGeneratorService} from
'@app/services/component-generator.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {HttpClientService} from '@app/services/http-client.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {AuthService} from '@app/services/auth.service';
+import {UtilsService} from '@app/services/utils.service';
import {DropdownListComponent} from './dropdown-list.component';
@@ -84,7 +84,6 @@ describe('DropdownListComponent', () => {
provide: HttpClientService,
useValue: httpClient
},
- ComponentActionsService,
HostsService,
AuditLogsService,
ServiceLogsService,
@@ -98,7 +97,8 @@ describe('DropdownListComponent', () => {
ComponentsService,
ServiceLogsTruncatedService,
TabsService,
- AuthService
+ AuthService,
+ UtilsService
]
})
.compileComponents();
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
index ef185d0..5d0ad4a 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/dropdown-list/dropdown-list.component.ts
@@ -16,33 +16,37 @@
* limitations under the License.
*/
-import {Component, AfterViewInit, Input, Output, EventEmitter, ViewChildren,
ViewContainerRef, QueryList} from '@angular/core';
+import {
+ Component, OnChanges, AfterViewChecked, SimpleChanges, Input, Output,
EventEmitter, ViewChildren, ViewContainerRef,
+ QueryList
+} from '@angular/core';
import {ListItem} from '@app/classes/list-item';
import {ComponentGeneratorService} from
'@app/services/component-generator.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
@Component({
selector: 'ul[data-component="dropdown-list"]',
templateUrl: './dropdown-list.component.html',
styleUrls: ['./dropdown-list.component.less']
})
-export class DropdownListComponent implements AfterViewInit {
+export class DropdownListComponent implements OnChanges, AfterViewChecked {
- constructor(private componentGenerator: ComponentGeneratorService, private
actions: ComponentActionsService) {
+ constructor(private componentGenerator: ComponentGeneratorService) {
}
- ngAfterViewInit() {
- const setter = this.additionalLabelComponentSetter;
- if (setter) {
- this.containers.forEach((container, index) =>
this.componentGenerator[setter](this.items[index].value, container));
+ private shouldRenderAdditionalComponents: boolean = false;
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.hasOwnProperty('items')) {
+ this.shouldRenderAdditionalComponents = true;
}
}
- @Input()
- items: ListItem[];
+ ngAfterViewChecked() {
+ this.renderAdditionalComponents();
+ }
@Input()
- defaultAction: Function;
+ items: ListItem[];
@Input()
isMultipleChoice?: boolean = false;
@@ -61,9 +65,18 @@ export class DropdownListComponent implements AfterViewInit {
})
containers: QueryList<ViewContainerRef>;
+ private renderAdditionalComponents(): void {
+ const setter = this.additionalLabelComponentSetter,
+ containers = this.containers;
+ if (this.shouldRenderAdditionalComponents && setter && containers) {
+ containers.forEach((container, index) =>
this.componentGenerator[setter](this.items[index].value, container));
+ this.shouldRenderAdditionalComponents = false;
+ }
+ }
+
changeSelectedItem(options: ListItem): void {
- if (options.action) {
- this.actions[options.action](...this.actionArguments);
+ if (options.onSelect) {
+ options.onSelect(...this.actionArguments);
}
this.selectedItemChange.emit(options);
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
index 4c93cbe..87c490c 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-button/filter-button.component.spec.ts
@@ -16,11 +16,10 @@
* limitations under the License.
*/
-import {NO_ERRORS_SCHEMA, Injector} from '@angular/core';
-import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TranslationModules} from '@app/test-config.spec';
import {StoreModule} from '@ngrx/store';
-import {ServiceInjector} from '@app/classes/service-injector';
import {AppSettingsService, appSettings} from
'@app/services/storage/app-settings.service';
import {ClustersService, clusters} from
'@app/services/storage/clusters.service';
import {ComponentsService, components} from
'@app/services/storage/components.service';
@@ -36,7 +35,6 @@ import {
} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {UtilsService} from '@app/services/utils.service';
import {HttpClientService} from '@app/services/http-client.service';
import {LogsContainerService} from '@app/services/logs-container.service';
@@ -91,7 +89,6 @@ describe('FilterButtonComponent', () => {
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
TabsService,
- ComponentActionsService,
UtilsService,
{
provide: HttpClientService,
@@ -105,12 +102,11 @@ describe('FilterButtonComponent', () => {
.compileComponents();
}));
- beforeEach(inject([Injector], (injector: Injector) => {
- ServiceInjector.injector = injector;
+ beforeEach(() => {
fixture = TestBed.createComponent(FilterButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
- }));
+ });
it('should create component', () => {
expect(component).toBeTruthy();
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
index 61d30d1..e3105c1 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.spec.ts
@@ -15,11 +15,10 @@
* limitations under the License.
*/
-import {NO_ERRORS_SCHEMA, Injector} from '@angular/core';
-import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TranslationModules} from '@app/test-config.spec';
import {StoreModule} from '@ngrx/store';
-import {ServiceInjector} from '@app/classes/service-injector';
import {AppSettingsService, appSettings} from
'@app/services/storage/app-settings.service';
import {AppStateService, appState} from
'@app/services/storage/app-state.service';
import {AuditLogsService, auditLogs} from
'@app/services/storage/audit-logs.service';
@@ -36,7 +35,6 @@ 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 {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 {AuthService} from '@app/services/auth.service';
@@ -111,7 +109,6 @@ describe('FilterDropdownComponent', () => {
useValue: filtering
},
UtilsService,
- ComponentActionsService,
LogsContainerService,
{
provide: HttpClientService,
@@ -124,12 +121,11 @@ describe('FilterDropdownComponent', () => {
.compileComponents();
}));
- beforeEach(inject([Injector], (injector: Injector) => {
- ServiceInjector.injector = injector;
+ beforeEach(() => {
fixture = TestBed.createComponent(FilterDropdownComponent);
component = fixture.componentInstance;
fixture.detectChanges();
- }));
+ });
it('should create component', () => {
expect(component).toBeTruthy();
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts
index 665386b..b85c572 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filter-dropdown/filter-dropdown.component.ts
@@ -17,7 +17,6 @@
import {Component, forwardRef} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
-import {UtilsService} from '@app/services/utils.service';
import {DropdownButtonComponent} from
'@app/components/dropdown-button/dropdown-button.component';
import {ListItem} from '@app/classes/list-item';
@@ -35,10 +34,6 @@ import {ListItem} from '@app/classes/list-item';
})
export class FilterDropdownComponent extends DropdownButtonComponent
implements ControlValueAccessor {
- constructor(protected utils: UtilsService) {
- super(utils);
- }
-
private onChange: (fn: any) => void;
get selection(): ListItem[] {
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
index f0cf3f4..51233ff 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.html
@@ -29,9 +29,9 @@
</button>
</div>
<div class="filter-buttons col-md-4">
- <dropdown-button [options]="searchBoxItems | async" iconClass="fa
fa-search-minus" action="proceedWithExclude"
- label="{{'filter.exclude' | translate}}"
[hideCaret]="true"
- [showSelectedValue]="false"></dropdown-button>
+ <dropdown-button iconClass="fa fa-search-minus" label="{{'filter.exclude'
| translate}}" [hideCaret]="true"
+ [showSelectedValue]="false"
[showCommonLabelWithSelection]="true"
+ [options]="searchBoxItems | async"
(selectItem)="proceedWithExclude($event)"></dropdown-button>
<filter-button *ngIf="isFilterConditionDisplayed('hosts')"
formControlName="hosts"
label="{{filters.hosts.label | translate}}"
[iconClass]="filters.hosts.iconClass"
[subItems]="filters.hosts.options"
[isMultipleChoice]="true" [isRightAlign]="true"
@@ -44,8 +44,8 @@
label="{{filters.levels.label | translate}}"
[iconClass]="filters.levels.iconClass"
[subItems]="filters.levels.options"
[isMultipleChoice]="true" [isRightAlign]="true"></filter-button>
<menu-button *ngIf="!captureSeconds" label="{{'filter.capture' |
translate}}" iconClass="fa fa-caret-right"
- action="startCapture"></menu-button>
+ (buttonClick)="startCapture()"></menu-button>
<menu-button *ngIf="captureSeconds" label="{{captureSeconds |
timerSeconds}}" iconClass="fa fa-stop stop-icon"
- action="stopCapture"></menu-button>
+ (buttonClick)="stopCapture()"></menu-button>
</div>
</form>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
index cd372ec..fe60671 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/filters-panel/filters-panel.component.ts
@@ -109,4 +109,21 @@ export class FiltersPanelComponent implements OnChanges {
this.searchBoxValueUpdate.next();
}
+ startCapture(): void {
+ this.logsContainer.startCaptureTimer();
+ }
+
+ stopCapture(): void {
+ this.logsContainer.stopCaptureTimer();
+ }
+
+ proceedWithExclude(item: string): void {
+ this.queryParameterNameChange.next({
+ item: {
+ value: item
+ },
+ isExclude: true
+ });
+ }
+
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.html
similarity index 71%
copy from
ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html
copy to
ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.html
index ab6326a..e6978f1 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.html
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<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" [badge]="item.badge"
[isRightAlign]="item.isRightAlign">
-</menu-button>
+
+<!-- TODO implement View details and Save filter actions -->
+<span class="fa fa-info-circle"></span>
+<span class="fa fa-floppy-o"></span>
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.less
similarity index 85%
copy from ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
copy to
ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.less
index 1aaaecc..dfb9997 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.less
@@ -16,11 +16,6 @@
* limitations under the License.
*/
-export interface ListItem {
- id?: string;
- label?: string;
- value: any;
- iconClass?: string;
- isChecked?: boolean;
- action?: string;
+:host {
+ float: right;
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.spec.ts
similarity index 70%
copy from
ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts
copy to
ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.spec.ts
index 081304e..4dbaa2d 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/action-menu/action-menu.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.spec.ts
@@ -16,27 +16,23 @@
* limitations under the License.
*/
-import {CUSTOM_ELEMENTS_SCHEMA} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
-import {TranslationModules} from '@app/test-config.spec';
-import {ActionMenuComponent} from './action-menu.component';
+import {HistoryItemControlsComponent} from './history-item-controls.component';
-describe('ActionMenuComponent', () => {
- let component: ActionMenuComponent;
- let fixture: ComponentFixture<ActionMenuComponent>;
+describe('HistoryItemControlsComponent', () => {
+ let component: HistoryItemControlsComponent;
+ let fixture: ComponentFixture<HistoryItemControlsComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
- imports: TranslationModules,
- declarations: [ActionMenuComponent],
- schemas: [CUSTOM_ELEMENTS_SCHEMA]
+ declarations: [HistoryItemControlsComponent]
})
.compileComponents();
}));
beforeEach(() => {
- fixture = TestBed.createComponent(ActionMenuComponent);
+ fixture = TestBed.createComponent(HistoryItemControlsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
diff --git a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.ts
similarity index 72%
copy from ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
copy to
ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.ts
index 1aaaecc..1975d9a 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/app/classes/list-item.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/history-item-controls/history-item-controls.component.ts
@@ -16,11 +16,13 @@
* limitations under the License.
*/
-export interface ListItem {
- id?: string;
- label?: string;
- value: any;
- iconClass?: string;
- isChecked?: boolean;
- action?: string;
+import {Component} from '@angular/core';
+
+@Component({
+ selector: 'history-item-controls',
+ templateUrl: './history-item-controls.component.html',
+ styleUrls: ['./history-item-controls.component.less']
+})
+export class HistoryItemControlsComponent {
+ // TODO implement View details and Save filter actions
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
index c346c9a..6e6a70e 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/log-context/log-context.component.spec.ts
@@ -37,6 +37,7 @@ import {TranslationModules} from '@app/test-config.spec';
import {ModalComponent} from '@app/components/modal/modal.component';
import {LogsContainerService} from '@app/services/logs-container.service';
import {HttpClientService} from '@app/services/http-client.service';
+import {UtilsService} from '@app/services/utils.service';
import {LogContextComponent} from './log-context.component';
@@ -94,7 +95,8 @@ describe('LogContextComponent', () => {
{
provide: HttpClientService,
useValue: httpClient
- }
+ },
+ UtilsService
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
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
index 5e2b15f..8e67b5d 100644
---
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
@@ -23,7 +23,9 @@
<i *ngIf="hasCaret" [ngClass]="['fa ', caretClass ]"></i>
<span *ngIf="label" class="menu-button-label">{{label}}</span>
</a>
- <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems"
(selectedItemChange)="onDropdownItemChange($event)"
- [isMultipleChoice]="isMultipleChoice"
[additionalLabelComponentSetter]="additionalLabelComponentSetter"
- [ngClass]="{'dropdown-menu': true, 'dropdown-menu-right':
isRightAlign}"></ul>
+ <ul data-component="dropdown-list" *ngIf="hasSubItems" [items]="subItems"
+ (selectedItemChange)="onDropdownItemChange($event)"
[isMultipleChoice]="isMultipleChoice"
+ [additionalLabelComponentSetter]="additionalLabelComponentSetter"
+ [ngClass]="'dropdown-menu' + (isRightAlign ? ' dropdown-menu-right' :
'') + (listClass ? ' ' + listClass : '')"
+ ></ul>
</div>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
index 3b210a3..4e77db5 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.spec.ts
@@ -16,10 +16,9 @@
* limitations under the License.
*/
-import {NO_ERRORS_SCHEMA, Injector} from '@angular/core';
-import {async, ComponentFixture, TestBed, inject} from '@angular/core/testing';
+import {NO_ERRORS_SCHEMA} from '@angular/core';
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {TranslationModules} from '@app/test-config.spec';
-import {ServiceInjector} from '@app/classes/service-injector';
import {StoreModule} from '@ngrx/store';
import {AppSettingsService, appSettings} from
'@app/services/storage/app-settings.service';
import {AppStateService, appState} from
'@app/services/storage/app-state.service';
@@ -36,7 +35,6 @@ import {
} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {HttpClientService} from '@app/services/http-client.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {AuthService} from '@app/services/auth.service';
@@ -90,7 +88,6 @@ describe('MenuButtonComponent', () => {
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
TabsService,
- ComponentActionsService,
{
provide: HttpClientService,
useValue: httpClient
@@ -103,12 +100,11 @@ describe('MenuButtonComponent', () => {
.compileComponents();
}));
- beforeEach(inject([Injector], (injector: Injector) => {
- ServiceInjector.injector = injector;
+ beforeEach(() => {
fixture = TestBed.createComponent(MenuButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
- }));
+ });
it('should create component', () => {
expect(component).toBeTruthy();
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
index 12da4ac..432b561 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/menu-button/menu-button.component.ts
@@ -16,10 +16,8 @@
* limitations under the License.
*/
-import {Component, Input, ViewChild, ElementRef} from '@angular/core';
+import {Component, Input, Output, ViewChild, ElementRef, EventEmitter} from
'@angular/core';
import {ListItem} from '@app/classes/list-item';
-import {ServiceInjector} from '@app/classes/service-injector';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
@Component({
selector: 'menu-button',
@@ -28,10 +26,6 @@ import {ComponentActionsService} from
'@app/services/component-actions.service';
})
export class MenuButtonComponent {
- constructor() {
- this.actions = ServiceInjector.injector.get(ComponentActionsService);
- }
-
@ViewChild('dropdown')
dropdown: ElementRef;
@@ -39,9 +33,6 @@ export class MenuButtonComponent {
label?: string;
@Input()
- action: string;
-
- @Input()
iconClass: string;
@Input()
@@ -84,7 +75,14 @@ export class MenuButtonComponent {
@Input()
maxLongClickDelay: number = 0;
- private actions: ComponentActionsService;
+ @Input()
+ listClass: string = '';
+
+ @Output()
+ buttonClick: EventEmitter<void> = new EventEmitter();
+
+ @Output()
+ selectItem: EventEmitter<ListItem> = new EventEmitter();
/**
* This is a private property to indicate the mousedown timestamp, so that
we can check it when teh click event
@@ -122,14 +120,14 @@ export class MenuButtonComponent {
!this.maxLongClickDelay || mdt + this.maxLongClickDelay >= now
);
let openDropdown = this.hasSubItems && (
- el.classList.contains(this.caretClass) || isLongClick ||
!this.actions[this.action]
+ el.classList.contains(this.caretClass) || isLongClick ||
!this.buttonClick.observers.length
);
if (openDropdown && this.dropdown) {
if (this.toggleDropdown()) {
this.listenToClickOut();
}
- } else if (this.action) {
- this.actions[this.action]();
+ } else if (this.buttonClick.observers.length) {
+ this.buttonClick.emit();
}
this.mouseDownTimestamp = 0;
event.preventDefault();
@@ -211,7 +209,7 @@ export class MenuButtonComponent {
}
updateSelection(options: ListItem) {
- // TODO implement value change behaviour
+ this.selectItem.emit(options);
}
}
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
index 02ed84b..ecfa75d 100644
---
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
@@ -17,7 +17,8 @@
<form class="pagination-form" [formGroup]="filtersForm">
<filter-dropdown label="{{filterInstance.label | translate}}"
formControlName="pageSize"
- [options]="filterInstance.options" [isRightAlign]="true"
[isDropup]="true"></filter-dropdown>
+ [options]="filterInstance.options" [isRightAlign]="true"
[isDropup]="true"
+ [showCommonLabelWithSelection]="true"></filter-dropdown>
<span>{{'pagination.numbers' | translate: numbersTranslateParams}}</span>
<pagination-controls formControlName="page" [totalCount]="totalCount"
[pagesCount]="pagesCount"
(currentPageChange)="setCurrentPage($event)"></pagination-controls>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
index 9dd8e26..ade57e5 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/search-box/search-box.component.ts
@@ -278,7 +278,7 @@ export class SearchBoxComponent implements OnInit,
OnDestroy, ControlValueAccess
updateValue = (): void => {
this.currentValue = '';
if (this.onChange) {
- this.onChange(this.parameters);
+ this.onChange(this.parameters.slice());
}
};
@@ -299,7 +299,7 @@ export class SearchBoxComponent implements OnInit,
OnDestroy, ControlValueAccess
}
writeValue(parameters: SearchBoxParameterProcessed[] = []): void {
- this.parameters = parameters;
+ this.parameters = parameters.slice();
this.updateValueSubject.next();
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html
index 3cd829e..3110d9a 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.html
@@ -23,12 +23,13 @@
{{'logs.brokenListLayoutMessage' | translate}}
</div>
<form *ngIf="logs && logs.length" [formGroup]="filtersForm">
- <filter-dropdown label="{{filters.serviceLogsSorting.label |
translate}}"
- formControlName="serviceLogsSorting"
[options]="filters.serviceLogsSorting.options" [isRightAlign]="true">
- </filter-dropdown>
+ <filter-dropdown formControlName="serviceLogsSorting"
label="{{filters.serviceLogsSorting.label | translate}}"
+ [showCommonLabelWithSelection]="true"
[options]="filters.serviceLogsSorting.options"
+ [isRightAlign]="true"></filter-dropdown>
</form>
<dropdown-button label="{{'logs.columns' | translate}}"
[options]="columns" [isRightAlign]="true"
- [isMultipleChoice]="true"
action="updateSelectedColumns" [additionalArgs]="logsTypeMapObject.fieldsModel">
+ [isMultipleChoice]="true"
(selectItem)="updateSelectedColumns($event)"
+ [listItemArguments]="logsTypeMapObject.fieldsModel">
</dropdown-button>
<div class="layout-btn-group">
<a *ngIf="layout==='FLEX'" class="btn" (click)="toggleShowLabels()"
tooltip="{{'logs.toggleLabels' | translate}}">
@@ -67,7 +68,7 @@
<tr class="log-item-row">
<td class="log-action">
<dropdown-button iconClass="fa fa-ellipsis-v action"
[hideCaret]="true" [options]="logActions"
- [additionalArgs]="[log]"
[showSelectedValue]="false"></dropdown-button>
+ [listItemArguments]="[log]"
[showSelectedValue]="false"></dropdown-button>
</td>
<td *ngIf="isColumnDisplayed('logtime')" class="log-time">
<time>
@@ -115,7 +116,7 @@
<div class="log-header">
<div class="log-action">
<dropdown-button iconClass="fa fa-ellipsis-v action"
[hideCaret]="true" [options]="logActions"
- [additionalArgs]="[log]"
[showSelectedValue]="false"></dropdown-button>
+ [listItemArguments]="[log]"
[showSelectedValue]="false"></dropdown-button>
</div>
<div *ngIf="isColumnDisplayed('level')" [ngClass]="'log-level '
+ log.level.toLowerCase()">
<log-level [logEntry]="log"></log-level>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts
index e883c99..e2afbc8 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.spec.ts
@@ -43,7 +43,6 @@ import {LogsContainerService} from
'@app/services/logs-container.service';
import {UtilsService} from '@app/services/utils.service';
import {HttpClientService} from '@app/services/http-client.service';
import {ComponentGeneratorService} from
'@app/services/component-generator.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {AuthService} from '@app/services/auth.service';
import {PaginationComponent} from
'@app/components/pagination/pagination.component';
import {DropdownListComponent} from
'@app/components/dropdown-list/dropdown-list.component';
@@ -113,7 +112,6 @@ describe('ServiceLogsTableComponent', () => {
ComponentsService,
HostsService,
ComponentGeneratorService,
- ComponentActionsService,
AuthService
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
index 681149d..ee19e1c 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/service-logs-table/service-logs-table.component.ts
@@ -16,10 +16,11 @@
* limitations under the License.
*/
-import {Component, AfterViewInit, AfterViewChecked, ViewChild, ElementRef,
Input, ChangeDetectorRef} from '@angular/core';
+import {Component, AfterViewChecked, ViewChild, ElementRef, Input,
ChangeDetectorRef} from '@angular/core';
import {ListItem} from '@app/classes/list-item';
import {LogsTableComponent} from
'@app/classes/components/logs-table/logs-table-component';
+import {ServiceLog} from '@app/classes/models/service-log';
import {LogsContainerService} from '@app/services/logs-container.service';
import {UtilsService} from '@app/services/utils.service';
@@ -100,53 +101,71 @@ export class ServiceLogsTableComponent extends
LogsTableComponent implements Aft
readonly timeFormat: string = 'h:mm:ss A';
+ private copyLog = (log: ServiceLog): void => {
+ if (document.queryCommandSupported('copy')) {
+ const text = log.log_message,
+ node = document.createElement('textarea');
+ node.value = text;
+ Object.assign(node.style, {
+ position: 'fixed',
+ top: '0',
+ left: '0',
+ width: '1px',
+ height: '1px',
+ border: 'none',
+ outline: 'none',
+ boxShadow: 'none',
+ backgroundColor: 'transparent',
+ padding: '0'
+ });
+ document.body.appendChild(node);
+ node.select();
+ if (document.queryCommandEnabled('copy')) {
+ document.execCommand('copy');
+ } else {
+ // TODO open failed alert
+ }
+ // TODO success alert
+ document.body.removeChild(node);
+ } else {
+ // TODO failed alert
+ }
+ };
+
+ private openLog = (log: ServiceLog): void => {
+ this.logsContainer.openServiceLog(log);
+ };
+
+ private openContext = (log: ServiceLog): void => {
+ this.logsContainer.loadLogContext(log.id, log.host, log.type);
+ };
+
readonly logActions = [
{
label: 'logs.copy',
iconClass: 'fa fa-files-o',
- action: 'copyLog'
+ onSelect: this.copyLog
},
{
label: 'logs.open',
iconClass: 'fa fa-external-link',
- action: 'openLog'
+ onSelect: this.openLog
},
{
label: 'logs.context',
iconClass: 'fa fa-crosshairs',
- action: 'openContext'
+ onSelect: this.openContext
}
];
readonly customStyledColumns: string[] = ['level', 'type', 'logtime',
'log_message', 'path'];
- get contextMenuItems(): ListItem[] {
- return this.logsContainer.queryContextMenuItems;
- }
-
private readonly messageFilterParameterName: string = 'log_message';
- /**
- * The goal is to show or hide the context menu on right click.
- * @type {boolean}
- */
- private isContextMenuDisplayed: boolean = false;
-
- /**
- * 'left' CSS property value for context menu dropdown
- * @type {number}
- */
- private contextMenuLeft: number = 0;
-
- /**
- * 'top' CSS property value for context menu dropdown
- * @type {number}
- */
- private contextMenuTop:number = 0;
+ private readonly logsType: string = 'serviceLogs';
private selectedText: string = '';
-
/**
* This is a private flag to store the table layout check result. It is used
to show user notifications about
* non-visible information.
@@ -154,6 +173,10 @@ export class ServiceLogsTableComponent extends
LogsTableComponent implements Aft
*/
private tooManyColumnsSelected: boolean = false;
+ get contextMenuItems(): ListItem[] {
+ return this.logsContainer.queryContextMenuItems;
+ }
+
get timeZone(): string {
return this.logsContainer.timeZone;
}
@@ -166,6 +189,22 @@ export class ServiceLogsTableComponent extends
LogsTableComponent implements Aft
return this.logsContainer.logsTypeMap.serviceLogs;
}
+ get isContextMenuDisplayed(): boolean {
+ return Boolean(this.selectedText);
+ };
+
+ /**
+ * 'left' CSS property value for context menu dropdown
+ * @type {number}
+ */
+ contextMenuLeft: number = 0;
+
+ /**
+ * 'top' CSS property value for context menu dropdown
+ * @type {number}
+ */
+ contextMenuTop: number = 0;
+
isDifferentDates(dateA, dateB): boolean {
return this.utils.isDifferentDates(dateA, dateB, this.timeZone);
}
@@ -173,7 +212,6 @@ export class ServiceLogsTableComponent extends
LogsTableComponent implements Aft
openMessageContextMenu(event: MouseEvent): void {
const selectedText = getSelection().toString();
if (selectedText) {
- this.isContextMenuDisplayed = true;
this.contextMenuLeft = event.clientX;
this.contextMenuTop = event.clientY;
this.selectedText = selectedText;
@@ -192,8 +230,7 @@ export class ServiceLogsTableComponent extends
LogsTableComponent implements Aft
/**
* Handle the event when the contextual menu component hide itself.
*/
- private onContextMenuDismiss = (): void => {
- this.isContextMenuDisplayed = false;
+ onContextMenuDismiss(): void {
this.selectedText = '';
};
@@ -264,4 +301,8 @@ export class ServiceLogsTableComponent extends
LogsTableComponent implements Aft
this.showLabels = !this.showLabels;
}
+ updateSelectedColumns(columns: string[]): void {
+ this.logsContainer.updateSelectedColumns(columns, this.logsType);
+ }
+
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts
index e3034b0..b41760f 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.spec.ts
@@ -37,6 +37,7 @@ import {ServiceLogsTruncatedService, serviceLogsTruncated}
from '@app/services/s
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {HttpClientService} from '@app/services/http-client.service';
import {LogsContainerService} from '@app/services/logs-container.service';
+import {UtilsService} from '@app/services/utils.service';
import {TimeRangePickerComponent} from './time-range-picker.component';
@@ -79,6 +80,7 @@ describe('TimeRangePickerComponent', () => {
useValue: httpClient
},
LogsContainerService,
+ UtilsService,
AppSettingsService,
AppStateService,
ClustersService,
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.ts
index 74a2b2d..6c71a26 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/time-range-picker/time-range-picker.component.ts
@@ -79,7 +79,7 @@ export class TimeRangePickerComponent implements
ControlValueAccessor {
setCustomTimeRange(): void {
this.selection = {
- label: 'filter.timeRange.custom',
+ label: this.logsContainer.customTimeRangeKey,
value: {
type: 'CUSTOM',
start: this.startTime,
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts
index 1772ec0..3d79b46 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.spec.ts
@@ -34,7 +34,6 @@ import {
} from '@app/services/storage/service-logs-histogram-data.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
import {HttpClientService} from '@app/services/http-client.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {AuthService} from '@app/services/auth.service';
@@ -94,7 +93,6 @@ describe('TimeZonePickerComponent', () => {
ServiceLogsHistogramDataService,
ServiceLogsTruncatedService,
TabsService,
- ComponentActionsService,
{
provide: HttpClientService,
useValue: httpClient
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts
index 32f6474..98758ab 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/timezone-picker/timezone-picker.component.ts
@@ -16,21 +16,23 @@
* limitations under the License.
*/
-import {Component} from '@angular/core';
+import {Component, OnInit} from '@angular/core';
import * as $ from 'jquery';
import '@vendor/js/WorldMapGenerator.min';
import {AppSettingsService} from '@app/services/storage/app-settings.service';
-import {ComponentActionsService} from
'@app/services/component-actions.service';
@Component({
selector: 'timezone-picker',
templateUrl: './timezone-picker.component.html',
styleUrls: ['./timezone-picker.component.less']
})
-export class TimeZonePickerComponent {
+export class TimeZonePickerComponent implements OnInit {
- constructor(private appSettings: AppSettingsService, private actions:
ComponentActionsService) {
- appSettings.getParameter('timeZone').subscribe(value => this.timeZone =
value);
+ constructor(private appSettings: AppSettingsService) {
+ }
+
+ ngOnInit() {
+ this.appSettings.getParameter('timeZone').subscribe((value: string) =>
this.timeZone = value);
}
readonly mapElementId = 'timezone-map';
@@ -70,7 +72,10 @@ export class TimeZonePickerComponent {
setTimeZone(): void {
const timeZone = this.timeZoneSelect.val();
- this.actions.setTimeZone(timeZone);
+
+ // TODO replace with setTimeZone() method call from settings service as
soon as it's implemented
+ this.appSettings.setParameter('timeZone', timeZone);
+
this.setTimeZonePickerDisplay(false);
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.html
b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.html
index 910e55f..71637bb 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.html
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.html
@@ -19,10 +19,10 @@
<form [formGroup]="filtersForm" class="filters">
<filter-dropdown *ngIf="isClustersFilterDisplayed"
formControlName="clusters" [options]="filters.clusters.options"
[isMultipleChoice]="true" label="{{filters.clusters.label
| translate}}" [isRightAlign]="true"
- buttonClass="inherited-color"></filter-dropdown>
+ buttonClass="btn-link inherited-color"></filter-dropdown>
</form>
- <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" [badge]="item.badge"
[isRightAlign]="item.isRightAlign">
+ <menu-button *ngFor="let item of items" label="{{item.label | translate}}"
[iconClass]="item.iconClass"
+ [labelClass]="item.labelClass" [subItems]="item.subItems"
[hideCaret]="item.hideCaret"
+ [badge]="item.badge" [isRightAlign]="item.isRightAlign">
</menu-button>
</div>
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
index ce4fa1c..caeb197 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.spec.ts
@@ -38,6 +38,8 @@ import {ComponentsService, components} from
'@app/services/storage/components.se
import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {HttpClientService} from '@app/services/http-client.service';
+import {AuthService} from '@app/services/auth.service';
+import {UtilsService} from '@app/services/utils.service';
import {TopMenuComponent} from './top-menu.component';
@@ -81,6 +83,8 @@ describe('TopMenuComponent', () => {
provide: HttpClientService,
useValue: httpClient
},
+ AuthService,
+ UtilsService,
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
index 8d739ec..1d003ee 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/components/top-menu/top-menu.component.ts
@@ -21,6 +21,7 @@ import {FormGroup} from '@angular/forms';
import {FilterCondition, TimeUnitListItem} from '@app/classes/filtering';
import {ListItem} from '@app/classes/list-item';
import {HomogeneousObject} from '@app/classes/object';
+import {AuthService} from '@app/services/auth.service';
import {LogsContainerService} from '@app/services/logs-container.service';
@Component({
@@ -30,7 +31,7 @@ import {LogsContainerService} from
'@app/services/logs-container.service';
})
export class TopMenuComponent {
- constructor(private logsContainer: LogsContainerService) {
+ constructor(private authService: AuthService, private logsContainer:
LogsContainerService) {
}
get filtersForm(): FormGroup {
@@ -41,7 +42,15 @@ export class TopMenuComponent {
return this.logsContainer.filters;
};
- //TODO implement loading of real data into subItems
+ openSettings = (): void => {};
+
+ /**
+ * Request a logout action from AuthService
+ */
+ logout = (): void => {
+ this.authService.logout();
+ };
+
readonly items = [
{
iconClass: 'fa fa-user grey',
@@ -49,11 +58,17 @@ export class TopMenuComponent {
isRightAlign: true,
subItems: [
{
- label: 'Options'
+ label: 'common.settings',
+ onSelect: this.openSettings,
+ iconClass: 'fa fa-cog'
+ },
+ {
+ isDivider: true
},
{
label: 'authorization.logout',
- action: 'logout'
+ onSelect: this.logout,
+ iconClass: 'fa fa-sign-out'
}
]
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
deleted file mode 100644
index 952c542..0000000
---
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.spec.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {TestBed, inject} from '@angular/core/testing';
-import {TranslationModules} from '@app/test-config.spec';
-import {StoreModule} from '@ngrx/store';
-import {AppSettingsService, appSettings} from
'@app/services/storage/app-settings.service';
-import {AppStateService, appState} from
'@app/services/storage/app-state.service';
-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 {AuditLogsGraphDataService, auditLogsGraphData} from
'@app/services/storage/audit-logs-graph-data.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 {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
-import {TabsService, tabs} from '@app/services/storage/tabs.service';
-import {HttpClientService} from '@app/services/http-client.service';
-import {LogsContainerService} from '@app/services/logs-container.service';
-import {AuthService} from '@app/services/auth.service';
-
-import {ComponentActionsService} from './component-actions.service';
-
-describe('ComponentActionsService', () => {
- const httpClient = {
- get: () => {
- return {
- subscribe: () => {
- }
- };
- }
- };
-
- beforeEach(() => {
- TestBed.configureTestingModule({
- imports: [
- StoreModule.provideStore({
- appSettings,
- appState,
- clusters,
- components,
- hosts,
- auditLogs,
- serviceLogs,
- auditLogsFields,
- auditLogsGraphData,
- serviceLogsFields,
- serviceLogsHistogramData,
- serviceLogsTruncated,
- tabs
- }),
- ...TranslationModules
- ],
- providers: [
- ComponentActionsService,
- AppSettingsService,
- AppStateService,
- ClustersService,
- ComponentsService,
- HostsService,
- AuditLogsService,
- ServiceLogsService,
- AuditLogsFieldsService,
- AuditLogsGraphDataService,
- ServiceLogsFieldsService,
- ServiceLogsHistogramDataService,
- ServiceLogsTruncatedService,
- TabsService,
- {
- provide: HttpClientService,
- useValue: httpClient
- },
- LogsContainerService,
- AuthService
- ]
- });
- });
-
- it('should create service', inject([ComponentActionsService], (service:
ComponentActionsService) => {
- expect(service).toBeTruthy();
- }));
-});
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
deleted file mode 100644
index 36c4d8d..0000000
---
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-actions.service.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import {Injectable} from '@angular/core';
-import {AppSettingsService} from '@app/services/storage/app-settings.service';
-import {TabsService} from '@app/services/storage/tabs.service';
-import {CollectionModelService} from '@app/classes/models/store';
-import {LogsContainerService} from '@app/services/logs-container.service';
-import {AuthService} from '@app/services/auth.service';
-import {ServiceLog} from '@app/classes/models/service-log';
-import {ListItem} from '@app/classes/list-item';
-
-@Injectable()
-export class ComponentActionsService {
-
- constructor(
- private appSettings: AppSettingsService, private tabsStorage: TabsService,
private authService: AuthService,
- private logsContainer: LogsContainerService
- ) {
- }
-
- //TODO implement actions
-
- undo() {
- }
-
- redo() {
- }
-
- refresh(): void {
- this.logsContainer.loadLogs();
- }
-
- openHistory() {
- }
-
- copyLog(log: ServiceLog): void {
- if (document.queryCommandSupported('copy')) {
- const text = log.log_message,
- node = document.createElement('textarea');
- node.value = text;
- Object.assign(node.style, {
- position: 'fixed',
- top: '0',
- left: '0',
- width: '1px',
- height: '1px',
- border: 'none',
- outline: 'none',
- boxShadow: 'none',
- backgroundColor: 'transparent',
- padding: '0'
- });
- document.body.appendChild(node);
- node.select();
- if (document.queryCommandEnabled('copy')) {
- document.execCommand('copy');
- } else {
- // TODO open failed alert
- }
- // TODO success alert
- document.body.removeChild(node);
- } else {
- // TODO failed alert
- }
- }
-
- openLog(log: ServiceLog): void {
- const tab = {
- id: log.id,
- isCloseable: true,
- label: `${log.host} >> ${log.type}`,
- appState: {
- activeLogsType: 'serviceLogs',
- isServiceLogsFileView: true,
- activeLog: {
- id: log.id,
- host_name: log.host,
- component_name: log.type
- },
- activeFilters:
Object.assign(this.logsContainer.getFiltersData('serviceLogs'), {
- components:
this.logsContainer.filters.components.options.find((option: ListItem): boolean
=> {
- return option.value === log.type;
- }),
- hosts: this.logsContainer.filters.hosts.options.find((option:
ListItem): boolean => {
- return option.value === log.host;
- })
- })
- }
- };
- this.tabsStorage.addInstance(tab);
- this.logsContainer.switchTab(tab);
- }
-
- openContext(log: ServiceLog): void {
- this.logsContainer.loadLogContext(log.id, log.host, log.type);
- }
-
- startCapture(): void {
- this.logsContainer.startCaptureTimer();
- }
-
- stopCapture(): void {
- this.logsContainer.stopCaptureTimer();
- }
-
- setTimeZone(timeZone: string): void {
- this.appSettings.setParameter('timeZone', timeZone);
- }
-
- updateSelectedColumns(columnNames: string[], model: CollectionModelService):
void {
- model.mapCollection(item => Object.assign({}, item, {
- isDisplayed: columnNames.indexOf(item.name) > -1
- }));
- }
-
- proceedWithExclude = (item: string): void =>
this.logsContainer.queryParameterNameChange.next({
- item: {
- value: item
- },
- isExclude: true
- });
-
- /**
- * Request a login action from the AuthService
- * @param {string} username
- * @param {string} password
- */
- login(username: string, password: string): void {
- this.authService.login(username, password);
- }
-
- /**
- * Request a logout action from AuthService
- */
- logout(): void {
- this.authService.logout();
- }
-
-}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
index a8ab5e8..b87fa8c 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
@@ -36,6 +36,7 @@ import {ServiceLogsTruncatedService, serviceLogsTruncated}
from '@app/services/s
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {HttpClientService} from '@app/services/http-client.service';
+import {UtilsService} from '@app/services/utils.service';
import {ComponentGeneratorService} from './component-generator.service';
@@ -75,6 +76,7 @@ describe('ComponentGeneratorService', () => {
provide: HttpClientService,
useValue: httpClient
},
+ UtilsService,
HostsService,
AuditLogsService,
ServiceLogsService,
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts
index 43755c0..1f82367 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.ts
@@ -21,6 +21,7 @@ import {HostsService} from
'@app/services/storage/hosts.service';
import {ComponentsService} from '@app/services/storage/components.service';
import {LogsContainerService} from '@app/services/logs-container.service';
import {NodeBarComponent} from '@app/components/node-bar/node-bar.component';
+import {HistoryItemControlsComponent} from
'@app/components/history-item-controls/history-item-controls.component';
@Injectable()
export class ComponentGeneratorService {
@@ -75,4 +76,9 @@ export class ComponentGeneratorService {
});
}
+ getHistoryItemIcons(historyItem, container: ViewContainerRef): void {
+ // TODO implement View details and Save filter actions
+ this.createComponent(HistoryItemControlsComponent, container);
+ }
+
}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/history-manager.service.spec.ts
similarity index 66%
copy from
ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
copy to
ambari-logsearch/ambari-logsearch-web/src/app/services/history-manager.service.spec.ts
index a8ab5e8..68a0e99 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/services/component-generator.service.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/services/history-manager.service.spec.ts
@@ -19,7 +19,6 @@
import {TestBed, inject} from '@angular/core/testing';
import {TranslationModules} from '@app/test-config.spec';
import {StoreModule} from '@ngrx/store';
-import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {AuditLogsService, auditLogs} from
'@app/services/storage/audit-logs.service';
import {ServiceLogsService, serviceLogs} from
'@app/services/storage/service-logs.service';
import {AuditLogsFieldsService, auditLogsFields} from
'@app/services/storage/audit-logs-fields.service';
@@ -32,14 +31,16 @@ import {AppSettingsService, appSettings} from
'@app/services/storage/app-setting
import {AppStateService, appState} from
'@app/services/storage/app-state.service';
import {ClustersService, clusters} from
'@app/services/storage/clusters.service';
import {ComponentsService, components} from
'@app/services/storage/components.service';
+import {HostsService, hosts} from '@app/services/storage/hosts.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
-import {LogsContainerService} from '@app/services/logs-container.service';
import {HttpClientService} from '@app/services/http-client.service';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {UtilsService} from '@app/services/utils.service';
-import {ComponentGeneratorService} from './component-generator.service';
+import {HistoryManagerService} from './history-manager.service';
-describe('ComponentGeneratorService', () => {
+describe('HistoryService', () => {
beforeEach(() => {
const httpClient = {
get: () => {
@@ -51,8 +52,8 @@ describe('ComponentGeneratorService', () => {
};
TestBed.configureTestingModule({
imports: [
+ ...TranslationModules,
StoreModule.provideStore({
- hosts,
auditLogs,
serviceLogs,
auditLogsFields,
@@ -63,19 +64,19 @@ describe('ComponentGeneratorService', () => {
appState,
clusters,
components,
+ hosts,
serviceLogsTruncated,
tabs
- }),
- ...TranslationModules
+ })
],
providers: [
- ComponentGeneratorService,
- LogsContainerService,
+ HistoryManagerService,
{
provide: HttpClientService,
useValue: httpClient
},
- HostsService,
+ LogsContainerService,
+ UtilsService,
AuditLogsService,
ServiceLogsService,
AuditLogsFieldsService,
@@ -86,13 +87,84 @@ describe('ComponentGeneratorService', () => {
AppStateService,
ClustersService,
ComponentsService,
+ HostsService,
ServiceLogsTruncatedService,
TabsService
]
});
});
- it('should create service', inject([ComponentGeneratorService], (service:
ComponentGeneratorService) => {
+ it('should be created', inject([HistoryManagerService], (service:
HistoryManagerService) => {
expect(service).toBeTruthy();
}));
+
+ describe('#isHistoryUnchanged()', () => {
+ const cases = [
+ {
+ valueA: {
+ p0: 'v0',
+ p1: ['v1'],
+ p2: {
+ k2: 'v2'
+ }
+ },
+ valueB: {
+ p0: 'v0',
+ p1: ['v1'],
+ p2: {
+ k2: 'v2'
+ }
+ },
+ result: true,
+ title: 'no difference'
+ },
+ {
+ valueA: {
+ p0: 'v0',
+ p1: ['v1'],
+ p2: {
+ k2: 'v2'
+ },
+ page: 0
+ },
+ valueB: {
+ p0: 'v0',
+ p1: ['v1'],
+ p2: {
+ k2: 'v2'
+ },
+ page: 1
+ },
+ result: true,
+ title: 'difference in ignored parameters'
+ },
+ {
+ valueA: {
+ p0: 'v0',
+ p1: ['v1'],
+ p2: {
+ k2: 'v2'
+ },
+ page: 0
+ },
+ valueB: {
+ p0: 'v0',
+ p1: ['v3'],
+ p2: {
+ k2: 'v4'
+ },
+ page: 1
+ },
+ result: false,
+ title: 'difference in non-ignored parameters'
+ }
+ ];
+
+ cases.forEach(test => {
+ it(test.title, inject([HistoryManagerService], (service:
HistoryManagerService) => {
+ const isHistoryUnchanged: (valueA: object, valueB: object) => boolean
= service['isHistoryUnchanged'];
+ expect(isHistoryUnchanged(test.valueA,
test.valueB)).toEqual(test.result);
+ }));
+ });
+ });
});
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/history-manager.service.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/history-manager.service.ts
new file mode 100644
index 0000000..39cadc6
--- /dev/null
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/services/history-manager.service.ts
@@ -0,0 +1,330 @@
+/**
+ * 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 {Injectable} from '@angular/core';
+import 'rxjs/add/operator/distinctUntilChanged';
+import 'rxjs/add/operator/takeUntil';
+import {TranslateService} from '@ngx-translate/core';
+import {SearchBoxParameter, TimeUnitListItem} from '@app/classes/filtering';
+import {ListItem} from '@app/classes/list-item';
+import {HomogeneousObject} from '@app/classes/object';
+import {History} from '@app/classes/models/app-state';
+import {Tab} from '@app/classes/models/tab';
+import {LogsContainerService} from '@app/services/logs-container.service';
+import {UtilsService} from '@app/services/utils.service';
+import {AppStateService} from '@app/services/storage/app-state.service';
+import {TabsService} from '@app/services/storage/tabs.service';
+
+@Injectable()
+export class HistoryManagerService {
+
+ constructor(
+ private translate: TranslateService, private logsContainer:
LogsContainerService, private utils: UtilsService,
+ private appState: AppStateService, private tabs: TabsService
+ ) {
+ // set labels for history list items
+ const filters = logsContainer.filters,
+ controlNames = Object.keys(filters).filter((name: string): boolean => {
+ const key = filters[name].label;
+ return key && this.ignoredParameters.indexOf(name) === -1;
+ }),
+ filterLabelKeys = controlNames.map((name: string): string =>
filters[name].label),
+ timeRangeLabels = filters.timeRange.options.reduce((
+ currentArray: string[], group: TimeUnitListItem[]
+ ): string[] => {
+ return [...currentArray, ...group.map((option: TimeUnitListItem):
string => option.label)];
+ }, [logsContainer.customTimeRangeKey]);
+
+ translate.get([
+ 'filter.include', 'filter.exclude', ...filterLabelKeys,
...timeRangeLabels
+ ]).subscribe((translates: object): void => {
+ this.controlNameLabels = controlNames.reduce((
+ currentObject: HomogeneousObject<string>, name: string
+ ): HomogeneousObject<string> => {
+ return Object.assign({}, currentObject, {
+ [name]: translates[filters[name].label]
+ })
+ }, {
+ include: translates['filter.include'],
+ exclude: translates['filter.exclude']
+ });
+ this.timeRangeLabels = timeRangeLabels.reduce((
+ currentObject: HomogeneousObject<string>, key: string
+ ): HomogeneousObject<string> => {
+ return Object.assign({}, currentObject, {
+ [key]: translates[key]
+ });
+ }, {});
+ });
+
+ // set default history state for each tab
+ tabs.mapCollection((tab: Tab): Tab => {
+ let currentAppState = tab.appState || {};
+ const appState = Object.assign({}, currentAppState, {
+ history: {
+ items: [],
+ currentId: -1
+ }
+ });
+ return Object.assign({}, tab, {
+ appState
+ });
+ });
+
+ // set current history items after switching tabs
+ appState.getParameter('history').subscribe((history: History): void => {
+ const filtersForm = logsContainer.filtersForm;
+ let defaultState;
+ if (history.items.length === 0) {
+ defaultState = filtersForm.value
+ }
+ this.activeHistory = history.items.slice();
+ this.currentHistoryItemId = history.currentId;
+
+ // handle filtering values changes
+ filtersForm.valueChanges
+ .distinctUntilChanged(this.isHistoryUnchanged)
+ .takeUntil(this.logsContainer.filtersFormChange)
+ .subscribe((value): void => {
+ if (this.hasNoPendingUndoOrRedo) {
+ const currentHistory = this.activeHistory,
+ previousValue = this.activeHistory.length ?
this.activeHistory[0].value.currentValue : defaultState,
+ isUndoOrRedo = value.isUndoOrRedo;
+ const previousChangeId = this.currentHistoryItemId;
+ if (isUndoOrRedo) {
+ this.hasNoPendingUndoOrRedo = false;
+ filtersForm.patchValue({
+ isUndoOrRedo: false
+ });
+ this.hasNoPendingUndoOrRedo = true;
+ } else {
+ this.currentHistoryItemId = currentHistory.length;
+ }
+ this.activeHistory = [
+ {
+ value: {
+ currentValue: Object.assign({}, value),
+ previousValue: Object.assign({}, previousValue),
+ changeId: this.currentHistoryItemId,
+ previousChangeId,
+ isUndoOrRedo
+ },
+ label: this.getHistoryItemLabel(previousValue, value)
+ },
+ ...currentHistory
+ ].slice(0, this.maxHistoryItemsCount);
+
+ // update history for active tab
+ this.tabs.mapCollection((tab: Tab): Tab => {
+ const currentAppState = tab.appState || {},
+ appState = Object.assign({}, currentAppState, tab.isActive ? {
+ history: {
+ items: this.activeHistory.slice(),
+ currentId: this.currentHistoryItemId
+ }
+ } : null);
+ return Object.assign({}, tab, {
+ appState
+ });
+ });
+ }
+ });
+ });
+ }
+
+ /**
+ * List of filter parameters which shouldn't affect changes history (related
to pagination and sorting)
+ * @type {string[]}
+ */
+ private readonly ignoredParameters: string[] = ['page', 'pageSize',
'auditLogsSorting', 'serviceLogsSorting'];
+
+ /**
+ * Maximal number of displayed history items
+ * @type {number}
+ */
+ private readonly maxHistoryItemsCount: number = 25;
+
+ /**
+ * Indicates whether there is no changes being applied to filters that are
triggered by undo or redo action.
+ * Since user can undo or redo several filters changes at once, and they are
applied to form controls step-by-step,
+ * this flag is needed to avoid recording intermediate items to history.
+ * @type {boolean}
+ */
+ private hasNoPendingUndoOrRedo: boolean = true;
+
+ /**
+ * Id of currently active history item.
+ * Generally speaking, it isn't id of the latest one because it can be
shifted by undo or redo action.
+ * @type {number}
+ */
+ private currentHistoryItemId: number = -1;
+
+ /**
+ * Contains i18n labels for filtering form control names
+ */
+ private controlNameLabels;
+
+ /**
+ * Contains i18n labels for time range options
+ */
+ private timeRangeLabels;
+
+ /**
+ * History items for current tab
+ * @type {Array}
+ */
+ activeHistory: ListItem[] = [];
+
+ /**
+ * List of filtering form control names for active tab
+ * @returns {Array}
+ */
+ private get filterParameters(): string[] {
+ return
this.logsContainer.logsTypeMap[this.logsContainer.activeLogsType].listFilters;
+ }
+
+ /**
+ * List of changes that can be undone
+ * @returns {ListItem[]}
+ */
+ get undoItems(): ListItem[] {
+ const allItems = this.activeHistory;
+ let startIndex = allItems.findIndex((item: ListItem): boolean => {
+ return item.value.changeId === this.currentHistoryItemId &&
!item.value.isUndoOrRedo;
+ }),
+ endIndex = allItems.slice(startIndex + 1).findIndex((item: ListItem):
boolean => item.value.isUndoOrRedo);
+ if (startIndex > -1) {
+ if (endIndex === -1) {
+ endIndex = allItems.length;
+ return allItems.slice(startIndex, startIndex + endIndex + 1);
+ }
+ } else {
+ return [];
+ }
+ }
+
+ /**
+ * List of changes that can be redone
+ * @returns {ListItem[]}
+ */
+ get redoItems(): ListItem[] {
+ const allItems = this.activeHistory.slice().reverse();
+ let startIndex = allItems.findIndex((item: ListItem): boolean => {
+ return item.value.previousChangeId === this.currentHistoryItemId &&
!item.value.isUndoOrRedo;
+ }),
+ endIndex = allItems.slice(startIndex + 1).findIndex((item: ListItem):
boolean => item.value.isUndoOrRedo);
+ if (startIndex === -1) {
+ startIndex = allItems.length;
+ }
+ if (endIndex === -1) {
+ endIndex = allItems.length;
+ }
+ return allItems.slice(startIndex, endIndex + startIndex + 1);
+ }
+
+ /**
+ * Indicates whether there are no filtering form changes that should be
tracked
+ * (all except the ones related to pagination and sorting)
+ * @param {object} valueA
+ * @param {object} valueB
+ * @returns {boolean}
+ */
+ private isHistoryUnchanged = (valueA: object, valueB: object): boolean => {
+ const objectA = Object.assign({}, valueA),
+ objectB = Object.assign({}, valueB);
+ this.ignoredParameters.forEach((controlName: string): void => {
+ delete objectA[controlName];
+ delete objectB[controlName];
+ });
+ return this.utils.isEqual(objectA, objectB);
+ };
+
+ /**
+ * Get label for certain form control change
+ * @param {string} controlName
+ * @param {any} selection
+ * @returns {string}
+ */
+ private getItemValueString(controlName: string, selection: any): string {
+ switch (controlName) {
+ case 'timeRange':
+ return `${this.controlNameLabels[controlName]}:
${this.timeRangeLabels[selection.label]}`;
+ case 'query':
+ const includes = selection.filter((item: SearchBoxParameter): boolean
=> {
+ return !item.isExclude;
+ }).map((item: SearchBoxParameter): string => `${item.name}:
${item.value}`).join(', '),
+ excludes = selection.filter((item: SearchBoxParameter): boolean => {
+ return item.isExclude;
+ }).map((item: SearchBoxParameter): string => `${item.name}:
${item.value}`).join(', '),
+ includesString = includes.length ?
`${this.controlNameLabels.include}: ${includes}` : '',
+ excludesString = excludes.length ?
`${this.controlNameLabels.exclude}: ${excludes}`: '';
+ return `${includesString} ${excludesString}`;
+ default:
+ const values = selection.map((option: ListItem) =>
option.value).join(', ');
+ return `${this.controlNameLabels[controlName]}: ${values}`;
+ }
+ }
+
+ /**
+ * Get label for history list item (i.e., difference with the previous one)
+ * @param {object} previousFormValue
+ * @param {object} currentFormValue
+ * @returns {string}
+ */
+ private getHistoryItemLabel(previousFormValue: object, currentFormValue:
object): string {
+ return this.filterParameters.reduce((currentResult: string, currentName:
string): string => {
+ const currentValue = currentFormValue[currentName];
+ if (this.ignoredParameters.indexOf(currentName) > -1
+ || this.utils.isEqual(previousFormValue[currentName], currentValue)) {
+ return currentResult;
+ } else {
+ const currentLabel = this.getItemValueString(currentName,
currentValue);
+ return `${currentResult} ${currentLabel}`;
+ }
+ }, '');
+ }
+
+ /**
+ * Handle undo or redo action correctly
+ * @param {object} value
+ */
+ private handleUndoOrRedo(value: object): void {
+ const filtersForm = this.logsContainer.filtersForm;
+ this.hasNoPendingUndoOrRedo = false;
+ this.filterParameters.forEach((controlName: string): void => {
+ if (this.ignoredParameters.indexOf(controlName) === -1) {
+ filtersForm.controls[controlName].setValue(value[controlName]);
+ }
+ });
+ this.hasNoPendingUndoOrRedo = true;
+ filtersForm.controls.isUndoOrRedo.setValue(true);
+ }
+
+ undo(item: ListItem): void {
+ this.hasNoPendingUndoOrRedo = false;
+ this.currentHistoryItemId = item.value.previousChangeId;
+ this.handleUndoOrRedo(item.value.previousValue);
+ }
+
+ redo(item: ListItem): void {
+ this.hasNoPendingUndoOrRedo = false;
+ this.currentHistoryItemId = item.value.changeId;
+ this.handleUndoOrRedo(item.value.currentValue);
+ }
+
+}
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
index 5961309..a8e7c3f 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.spec.ts
@@ -35,6 +35,7 @@ import {HostsService, hosts} from
'@app/services/storage/hosts.service';
import {ServiceLogsTruncatedService, serviceLogsTruncated} from
'@app/services/storage/service-logs-truncated.service';
import {TabsService, tabs} from '@app/services/storage/tabs.service';
import {HttpClientService} from '@app/services/http-client.service';
+import {UtilsService} from '@app/services/utils.service';
import {ListItem} from '@app/classes/list-item';
import {NodeItem} from '@app/classes/models/node-item';
@@ -87,7 +88,8 @@ describe('LogsContainerService', () => {
{
provide: HttpClientService,
useValue: httpClient
- }
+ },
+ UtilsService
]
});
});
diff --git
a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
index baa3972..6a2108b 100644
---
a/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
+++
b/ambari-logsearch/ambari-logsearch-web/src/app/services/logs-container.service.ts
@@ -23,11 +23,13 @@ import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/timer';
import 'rxjs/add/observable/combineLatest';
+import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/first';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/takeUntil';
import * as moment from 'moment-timezone';
import {HttpClientService} from '@app/services/http-client.service';
+import {UtilsService} from '@app/services/utils.service';
import {AuditLogsService} from '@app/services/storage/audit-logs.service';
import {AuditLogsFieldsService} from
'@app/services/storage/audit-logs-fields.service';
import {AuditLogsGraphDataService} from
'@app/services/storage/audit-logs-graph-data.service';
@@ -62,13 +64,13 @@ import {CommonEntry} from
'@app/classes/models/common-entry';
export class LogsContainerService {
constructor(
- private httpClient: HttpClientService, private tabsStorage: TabsService,
private componentsStorage: ComponentsService,
- private hostsStorage: HostsService, private appState: AppStateService,
private auditLogsStorage: AuditLogsService,
+ private httpClient: HttpClientService, private utils: UtilsService,
+ private tabsStorage: TabsService, private componentsStorage:
ComponentsService, private hostsStorage: HostsService,
+ private appState: AppStateService, private auditLogsStorage:
AuditLogsService,
private auditLogsGraphStorage: AuditLogsGraphDataService, private
auditLogsFieldsStorage: AuditLogsFieldsService,
private serviceLogsStorage: ServiceLogsService, private
serviceLogsFieldsStorage: ServiceLogsFieldsService,
private serviceLogsHistogramStorage: ServiceLogsHistogramDataService,
private clustersStorage: ClustersService,
private serviceLogsTruncatedStorage: ServiceLogsTruncatedService, private
appSettings: AppSettingsService
-
) {
const formItems = Object.keys(this.filters).reduce((currentObject: any,
key: string): HomogeneousObject<FormControl> => {
let formControl = new FormControl(),
@@ -104,17 +106,20 @@ export class LogsContainerService {
});
}
this.loadLogs();
-
this.filtersForm.valueChanges.takeUntil(this.filtersFormChange).subscribe((value:
object): void => {
- this.tabsStorage.mapCollection((tab: Tab): Tab => {
- const currentAppState = tab.appState || {},
- appState = Object.assign({}, currentAppState, tab.isActive ? {
- activeFilters: value
- } : null);
- return Object.assign({}, tab, {
- appState
+ this.filtersForm.valueChanges
+ .distinctUntilChanged(this.isFormUnchanged)
+ .takeUntil(this.filtersFormChange)
+ .subscribe((value): void => {
+ this.tabsStorage.mapCollection((tab: Tab): Tab => {
+ const currentAppState = tab.appState || {},
+ appState = Object.assign({}, currentAppState, tab.isActive ? {
+ activeFilters: value
+ } : null);
+ return Object.assign({}, tab, {
+ appState
+ });
});
- });
- this.loadLogs();
+ this.loadLogs();
});
});
}
@@ -129,6 +134,7 @@ export class LogsContainerService {
fieldName: 'cluster'
},
timeRange: {
+ label: 'filter.duration',
options: [
[
{
@@ -473,7 +479,12 @@ export class LogsContainerService {
page: {
defaultSelection: 0
},
- query: {}
+ query: {
+ defaultSelection: []
+ },
+ isUndoOrRedo: {
+ defaultSelection: false
+ }
};
readonly colors = {
@@ -508,6 +519,8 @@ export class LogsContainerService {
query: ['includeQuery', 'excludeQuery']
};
+ readonly customTimeRangeKey: string = 'filter.timeRange.custom';
+
readonly topResourcesCount: string = '10';
readonly topUsersCount: string = '6';
@@ -569,7 +582,7 @@ export class LogsContainerService {
activeLogsType: LogsType;
- private filtersFormChange: Subject<void> = new Subject();
+ filtersFormChange: Subject<void> = new Subject();
private columnsMapper<FieldT extends LogField>(fields: FieldT[]): ListItem[]
{
return fields.filter((field: FieldT): boolean =>
field.isAvailable).map((field: FieldT): ListItem => {
@@ -649,6 +662,16 @@ export class LogsContainerService {
topResourcesGraphData: HomogeneousObject<HomogeneousObject<number>> = {};
+ private isFormUnchanged = (valueA: object, valueB: object): boolean => {
+ const trackedControlNames =
this.logsTypeMap[this.activeLogsType].listFilters;
+ for (let name of trackedControlNames) {
+ if (!this.utils.isEqual(valueA[name], valueB[name])) {
+ return false;
+ }
+ }
+ return true;
+ };
+
loadLogs = (logsType: LogsType = this.activeLogsType): void => {
this.httpClient.get(logsType,
this.getParams('listFilters')).subscribe((response: Response): void => {
const jsonResponse = response.json(),
@@ -992,7 +1015,7 @@ export class LogsContainerService {
setCustomTimeRange(startTime: number, endTime: number): void {
this.filtersForm.controls.timeRange.setValue({
- label: 'filter.timeRange.custom',
+ label: this.customTimeRangeKey,
value: {
type: 'CUSTOM',
start: moment(startTime),
@@ -1016,4 +1039,37 @@ export class LogsContainerService {
&& Boolean(this.filtersForm.controls[key]);
}
+ updateSelectedColumns(columnNames: string[], logsType: string): void {
+ this.logsTypeMap[logsType].fieldsModel.mapCollection(item =>
Object.assign({}, item, {
+ isDisplayed: columnNames.indexOf(item.name) > -1
+ }));
+ }
+
+ openServiceLog(log: ServiceLog): void {
+ const tab = {
+ id: log.id,
+ isCloseable: true,
+ label: `${log.host} >> ${log.type}`,
+ appState: {
+ activeLogsType: 'serviceLogs',
+ isServiceLogsFileView: true,
+ activeLog: {
+ id: log.id,
+ host_name: log.host,
+ component_name: log.type
+ },
+ activeFilters: Object.assign(this.getFiltersData('serviceLogs'), {
+ components: this.filters.components.options.find((option: ListItem):
boolean => {
+ return option.value === log.type;
+ }),
+ hosts: this.filters.hosts.options.find((option: ListItem): boolean
=> {
+ return option.value === log.host;
+ })
+ })
+ }
+ };
+ this.tabsStorage.addInstance(tab);
+ this.switchTab(tab);
+ }
+
}
diff --git a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
index 2b34b4d..1d8f6c4 100644
--- a/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
+++ b/ambari-logsearch/ambari-logsearch-web/src/assets/i18n/en.json
@@ -4,6 +4,9 @@
"common.auditLogs": "Audit Logs",
"common.summary": "Summary",
"common.logs": "Logs",
+ "common.name": "Name",
+ "common.value": "Value",
+ "common.settings": "Settings",
"modal.submit": "OK",
"modal.cancel": "Cancel",
@@ -26,8 +29,10 @@
"filter.clusters": "Clusters",
"filter.components": "Components",
"filter.levels": "Levels",
+ "filter.include": "Include",
"filter.exclude": "Exclude",
"filter.hosts": "Hosts",
+ "filter.duration": "Duration",
"filter.capture": "Capture",
"filter.capture.triggeringRefresh": "Triggering auto-refresh in
{{remainingSeconds}} sec",
--
To stop receiving notification emails like this one, please contact
[email protected].