Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

METRON-2335: [UI] Implement synchronization between browser user state and Hbase user state #1575

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ describe('Automatic data polling on Alerts View', () => {

cy.route('GET', '/api/v1/global/config', 'fixture:config.json');
cy.route('GET', appConfigJSON.contextMenuConfigURL, 'fixture:context-menu.conf.json');

cy.route({
url: '/api/v1/hdfs?path=user-settings',
method: 'GET',
response: {},
});

cy.route({
url: '/api/v1/hdfs?path=user-settings',
method: 'POST',
response: {},
});
};

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import { SearchService } from 'app/service/search.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { AppConfigService } from 'app/service/app-config.service';
import { GlobalConfigService } from 'app/service/global-config.service';
import { DataSource } from 'app/service/data-source';
import { ElasticSearchLocalstorageImpl } from 'app/service/elasticsearch-localstorage-impl';
import { DialogService } from 'app/service/dialog.service';
import { By } from '@angular/platform-browser';
import { AlertComment } from './alert-comment';
Expand All @@ -39,6 +37,7 @@ import { CommentAddRemoveRequest } from '../../model/comment-add-remove-request'
import { AlertSource } from '../../model/alert-source';
import { of } from 'rxjs/index';
import { Router } from '@angular/router';
import { UserSettingsService } from 'app/service/user-settings.service';

const alertDetail = {
'enrichments:geo:ip_dst_addr:locID': '5308655',
Expand Down Expand Up @@ -126,10 +125,7 @@ describe('AlertDetailsComponent', () => {
appConfigStatic: {},
getApiRoot: () => {},
} },
{
provide: DataSource,
useClass: ElasticSearchLocalstorageImpl
},
UserSettingsService,
{ provide: AlertsService,
useValue: {
escalate: () => {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import { DialogService } from 'app/service/dialog.service';
import { RestError } from 'app/model/rest-error';
import { DialogType } from 'app/model/dialog-type';
import { RefreshInterval } from 'app/alerts/configure-rows/configure-rows-enums';
import { UserSettingsService } from 'app/service/user-settings.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { AppConfigService } from 'app/service/app-config.service';

const DEFAULT_POLLING_INTERVAL = RefreshInterval.TEN_MIN;

Expand Down Expand Up @@ -63,11 +66,18 @@ describe('AutoPollingService', () => {
}

beforeEach(() => {
localStorage.getItem = () => null;
localStorage.setItem = () => {};

TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
],
providers: [
{
provide: AppConfigService,
useValue: {
getApiRoot() { return ''; }
}
},
UserSettingsService,
AutoPollingService,
{ provide: DialogService, useClass: () => {} },
{ provide: SearchService, useClass: () => { return {
Expand Down Expand Up @@ -447,35 +457,27 @@ describe('AutoPollingService', () => {

describe('polling state persisting and restoring', () => {

it('should persist polling state on start', () => {
spyOn(localStorage, 'setItem');
autoPollingService.start();
expect(localStorage.setItem).toHaveBeenCalledWith('autoPolling', `{"isActive":true,"refreshInterval":${DEFAULT_POLLING_INTERVAL}}`);
});

it('should persist polling state on stop', () => {
spyOn(localStorage, 'setItem');
autoPollingService.stop();
expect(localStorage.setItem).toHaveBeenCalledWith('autoPolling', `{"isActive":false,"refreshInterval":${DEFAULT_POLLING_INTERVAL}}`);
});

it('should persist polling state on interval change', () => {
spyOn(localStorage, 'setItem');
const userSettingsService = TestBed.get(UserSettingsService);
spyOn(userSettingsService, 'save').and.callThrough();
autoPollingService.setInterval(4);
expect(localStorage.setItem).toHaveBeenCalledWith('autoPolling', '{"isActive":false,"refreshInterval":4}');
expect(userSettingsService.save).toHaveBeenCalledWith({
autoPolling: '{"isActive":false,"refreshInterval":4}'
});
});

it('should restore polling state on construction with a delay', fakeAsync(() => {
const queryBuilderFake = TestBed.get(QueryBuilder);
const dialogServiceFake = TestBed.get(DialogService);
const userSettingsService = TestBed.get(UserSettingsService);

spyOn(localStorage, 'getItem').and.returnValue('{"isActive":true,"refreshInterval":443}');
spyOn(userSettingsService, 'get').and.returnValue(of('{"isActive":true,"refreshInterval":443}'));

const localAutoPollingSvc = new AutoPollingService(searchServiceFake, queryBuilderFake, dialogServiceFake);
const localAutoPollingSvc = new AutoPollingService(searchServiceFake, queryBuilderFake, dialogServiceFake, userSettingsService);

tick(localAutoPollingSvc.AUTO_START_DELAY);

expect(localStorage.getItem).toHaveBeenCalledWith('autoPolling');
expect(userSettingsService.get).toHaveBeenCalledWith('autoPolling');
expect(localAutoPollingSvc.getIsPollingActive()).toBe(true);
expect(localAutoPollingSvc.getInterval()).toBe(443);

Expand All @@ -485,11 +487,12 @@ describe('AutoPollingService', () => {
it('should start polling on construction when persisted isActive==true', fakeAsync(() => {
const queryBuilderFake = TestBed.get(QueryBuilder);
const dialogServiceFake = TestBed.get(DialogService);
const userSettingsService = TestBed.get(UserSettingsService);

spyOn(searchServiceFake, 'search').and.callThrough();
spyOn(localStorage, 'getItem').and.returnValue('{"isActive":true,"refreshInterval":10}');
spyOn(userSettingsService, 'get').and.returnValue(of('{"isActive":true,"refreshInterval":10}'));

const localAutoPollingSvc = new AutoPollingService(searchServiceFake, queryBuilderFake, dialogServiceFake);
const localAutoPollingSvc = new AutoPollingService(searchServiceFake, queryBuilderFake, dialogServiceFake, userSettingsService);

tick(localAutoPollingSvc.AUTO_START_DELAY);

Expand All @@ -507,11 +510,12 @@ describe('AutoPollingService', () => {
it('should start polling on construction with the persisted interval', fakeAsync(() => {
const queryBuilderFake = TestBed.get(QueryBuilder);
const dialogServiceFake = TestBed.get(DialogService);
const userSettingsService = TestBed.get(UserSettingsService);

spyOn(searchServiceFake, 'search').and.callThrough();
spyOn(localStorage, 'getItem').and.returnValue('{"isActive":true,"refreshInterval":4}');
spyOn(userSettingsService, 'get').and.returnValue(of('{"isActive":true,"refreshInterval":4}'));

const localAutoPollingSvc = new AutoPollingService(searchServiceFake, queryBuilderFake, dialogServiceFake);
const localAutoPollingSvc = new AutoPollingService(searchServiceFake, queryBuilderFake, dialogServiceFake, userSettingsService);

tick(localAutoPollingSvc.AUTO_START_DELAY);

Expand All @@ -526,5 +530,4 @@ describe('AutoPollingService', () => {
localAutoPollingSvc.stop();
}));
});

});
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { Subscription, Subject, Observable, interval, onErrorResumeNext, timer } from 'rxjs';
import { Subscription, Subject, Observable, interval, timer } from 'rxjs';
import { SearchService } from 'app/service/search.service';
import { QueryBuilder } from '../query-builder';
import { SearchResponse } from 'app/model/search-response';
Expand All @@ -26,6 +26,7 @@ import { RestError } from 'app/model/rest-error';
import { DialogType } from 'app/shared/metron-dialog/metron-dialog.component';
import { DialogService } from 'app/service/dialog.service';
import { RefreshInterval } from '../../configure-rows/configure-rows-enums';
import { UserSettingsService } from 'app/service/user-settings.service';

interface AutoPollingStateModel {
isActive: boolean,
Expand All @@ -49,6 +50,7 @@ export class AutoPollingService {
constructor(private searchService: SearchService,
private queryBuilder: QueryBuilder,
private dialogService: DialogService,
private userSettingsService: UserSettingsService
) {
this.restoreState();
}
Expand Down Expand Up @@ -108,19 +110,24 @@ export class AutoPollingService {
}

private persistState(key = this.AUTO_POLLING_STORAGE_KEY): void {
localStorage.setItem(key, JSON.stringify(this.getStateModel()));
this.userSettingsService.save({
[key]: JSON.stringify(this.getStateModel())
}).subscribe();
}

private restoreState(key = this.AUTO_POLLING_STORAGE_KEY): void {
const persistedState = JSON.parse(localStorage.getItem(key)) as AutoPollingStateModel;

if (persistedState) {
this.refreshInterval = persistedState.refreshInterval;

if (persistedState.isActive) {
timer(this.AUTO_START_DELAY).subscribe(this.start.bind(this));
}
}
this.userSettingsService.get(key)
.subscribe((autoPollingState) => {
const persistedState = autoPollingState ? JSON.parse(autoPollingState) as AutoPollingStateModel : null;

if (persistedState) {
this.refreshInterval = persistedState.refreshInterval;

if (persistedState.isActive) {
timer(this.AUTO_START_DELAY).subscribe(this.start.bind(this));
}
}
});
}

private getStateModel(): AutoPollingStateModel {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { DialogService } from 'app/service/dialog.service';
import { AppConfigService } from '../../../service/app-config.service';
import { ContextMenuComponent } from 'app/shared/context-menu/context-menu.component';
import { of } from 'rxjs';
import { UserSettingsService } from 'app/service/user-settings.service';

@Component({selector: 'metron-table-pagination', template: ''})
class MetronTablePaginationComponent {
Expand Down Expand Up @@ -63,7 +64,8 @@ describe('TableViewComponent', () => {
{ provide: AppConfigService, useClass: FakeAppConfigService },
{ provide: GlobalConfigService, useValue: {
get: () => { return of({})}
}}
}},
UserSettingsService
],
declarations: [
MetronTableDirective,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { MetaAlertService } from '../../../service/meta-alert.service';
import { DialogService } from 'app/service/dialog.service';
import { AppConfigService } from '../../../service/app-config.service';
import { of } from 'rxjs';
import { UserSettingsService } from 'app/service/user-settings.service';

class FakeAppConfigService {

Expand All @@ -57,7 +58,8 @@ describe('TreeViewComponent', () => {
{ provide: AppConfigService, useClass: FakeAppConfigService },
{ provide: GlobalConfigService, useValue: {
get: () => { return of({})}
}}
}},
UserSettingsService
],
declarations: [
MetronTableDirective,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ import { QueryBuilder } from '../alerts-list/query-builder';
import { ShowHideService } from './show-hide/show-hide.service';
import { TimezoneConfigComponent } from './timezone-config/timezone-config.component';
import { TimezoneConfigService } from './timezone-config/timezone-config.service';
import { AppConfigService } from 'app/service/app-config.service';
import { UserSettingsService } from 'app/service/user-settings.service';

@NgModule({
imports: [ SharedModule, SwitchModule ],
declarations: [ ConfigureRowsComponent, ShowHideAlertEntriesComponent, TimezoneConfigComponent ],
exports: [ ConfigureRowsComponent ],
providers: [ QueryBuilder, ShowHideService, TimezoneConfigService, ],
providers: [ AppConfigService, UserSettingsService, QueryBuilder, ShowHideService, TimezoneConfigService, ],
})
export class ConfigureRowsModule {

constructor(private showHideService: ShowHideService, private timezoneConfigService: TimezoneConfigService) {}

}
export class ConfigureRowsModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import { QueryBuilder, FilteringMode } from 'app/alerts/alerts-list/query-builde

import { Spy } from 'jasmine-core';
import { Filter } from 'app/model/filter';
import { serializePath } from '@angular/router/src/url_tree';
import { UserSettingsService } from 'app/service/user-settings.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { AppConfigService } from 'app/service/app-config.service';

class QueryBuilderMock {
addOrUpdateFilter = () => {};
Expand All @@ -32,21 +34,31 @@ class QueryBuilderMock {

describe('ShowHideService', () => {
let queryBuilderMock: QueryBuilderMock;
let userSettingsService: UserSettingsService;

beforeEach(() => {
spyOn(localStorage, 'getItem').and.returnValues('true', 'false');
spyOn(localStorage, 'setItem');

spyOn(ShowHideService.prototype, 'setFilterFor').and.callThrough();
spyOn(UserSettingsService.prototype, 'get').and.callThrough();
spyOn(UserSettingsService.prototype, 'save').and.callThrough();

TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [
{
provide: AppConfigService,
useValue: {
getApiRoot() { return ''; }
}
},
UserSettingsService,
ShowHideService,
{ provide: QueryBuilder, useClass: QueryBuilderMock },
]
});

queryBuilderMock = getTestBed().get(QueryBuilder);
userSettingsService = getTestBed().get(UserSettingsService);
});

it('should be created', inject([ShowHideService], (service: ShowHideService) => {
Expand All @@ -57,36 +69,38 @@ describe('ShowHideService', () => {
expect(service.queryBuilder).toBeTruthy();
}));

it('should get persisted state from localStorage', inject([ShowHideService], (service: ShowHideService) => {
expect(localStorage.getItem).toHaveBeenCalledWith(service.HIDE_RESOLVE_STORAGE_KEY);
expect(localStorage.getItem).toHaveBeenCalledWith(service.HIDE_DISMISS_STORAGE_KEY);
it('should get persisted state', inject([ShowHideService], (service: ShowHideService) => {
expect(userSettingsService.get).toHaveBeenCalledWith(service.HIDE_RESOLVE_STORAGE_KEY);
expect(userSettingsService.get).toHaveBeenCalledWith(service.HIDE_DISMISS_STORAGE_KEY);
}));

it('should set initial filter state', inject([ShowHideService], (service: ShowHideService) => {
expect((service.setFilterFor as Spy).calls.argsFor(0)[1]).toBe(true);
expect((service.setFilterFor as Spy).calls.argsFor(0)[0]).toBe('RESOLVE');
expect((service.setFilterFor as Spy).calls.argsFor(1)[0]).toBe('DISMISS');
expect((service.setFilterFor as Spy).calls.argsFor(1)[1]).toBe(false);
expect((service.setFilterFor as Spy).calls.argsFor(0)).toEqual(['RESOLVE', false]);
expect((service.setFilterFor as Spy).calls.argsFor(1)).toEqual(['DISMISS', false]);
}));

it('should set value loaded from localStorage to hideDismissed ', inject([ShowHideService], (service: ShowHideService) => {
it('should set value to hideDismissed ', inject([ShowHideService], (service: ShowHideService) => {
expect(service.hideDismissed).toBe(false);
}));

it('should set value loaded from localStorage to hideResolved', inject([ShowHideService], (service: ShowHideService) => {
expect(service.hideResolved).toBe(true);
it('should set value to hideResolved', inject([ShowHideService], (service: ShowHideService) => {
expect(service.hideResolved).toBe(false);
}));

it('should save state to localStorage on change for RESOLVE', inject([ShowHideService], (service: ShowHideService) => {
it('should save state on change for RESOLVE', inject([ShowHideService], (service: ShowHideService) => {
service.setFilterFor('RESOLVE', true);

expect(localStorage.setItem).toHaveBeenCalledWith(service.HIDE_RESOLVE_STORAGE_KEY, true);
expect(userSettingsService.save).toHaveBeenCalledWith({
[service.HIDE_RESOLVE_STORAGE_KEY]: true
});
}));

it('should save state to localStorage on change for DISMISS', inject([ShowHideService], (service: ShowHideService) => {
it('should save state for DISMISS', inject([ShowHideService], (service: ShowHideService) => {
service.setFilterFor('DISMISS', true);

expect(localStorage.setItem).toHaveBeenCalledWith(service.HIDE_DISMISS_STORAGE_KEY, true);
expect(userSettingsService.save).toHaveBeenCalledWith({
[service.HIDE_DISMISS_STORAGE_KEY]: true
});
}));

it('should be able to add RESOLVE filter to QueryBuilder', inject([ShowHideService], (service: ShowHideService) => {
Expand Down
Loading