http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts new file mode 100644 index 0000000..5534bea --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.spec.ts @@ -0,0 +1,742 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {SpyLocation} from '@angular/common/testing'; +import {Http, ResponseOptions, RequestOptions, Response} from '@angular/http'; + +import {DebugElement, Inject} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {Router, NavigationStart} from '@angular/router'; +import {Observable} from 'rxjs/Observable'; +import {SensorParserListComponent} from './sensor-parser-list.component'; +import {SensorParserConfigService} from '../../service/sensor-parser-config.service'; +import {MetronAlerts} from '../../shared/metron-alerts'; +import {TopologyStatus} from '../../model/topology-status'; +import {SensorParserConfig} from '../../model/sensor-parser-config'; +import {AuthenticationService} from '../../service/authentication.service'; +import {SensorParserListModule} from './sensor-parser-list.module'; +import {MetronDialogBox} from '../../shared/metron-dialog-box'; +import {Sort} from '../../util/enums'; +import 'jquery'; +import {SensorParserConfigHistoryService} from '../../service/sensor-parser-config-history.service'; +import {SensorParserConfigHistory} from '../../model/sensor-parser-config-history'; +import {APP_CONFIG, METRON_REST_CONFIG} from '../../app.config'; +import {StormService} from '../../service/storm.service'; +import {IAppConfig} from '../../app.config.interface'; + +class MockAuthenticationService extends AuthenticationService { + + constructor(private http2: Http, private router2: Router, @Inject(APP_CONFIG) private config2: IAppConfig) { + super(http2, router2, config2); + } + + public checkAuthentication() { + } + + public getCurrentUser(options: RequestOptions): Observable<Response> { + return Observable.create(observer => { + observer.next(new Response(new ResponseOptions({body: 'test'}))); + observer.complete(); + }); + } +} + +class MockSensorParserConfigHistoryService extends SensorParserConfigHistoryService { + + private allSensorParserConfigHistory: SensorParserConfigHistory[]; + + constructor(private http2: Http, @Inject(APP_CONFIG) private config2: IAppConfig) { + super(http2, config2); + } + + public setSensorParserConfigHistoryForTest(allSensorParserConfigHistory: SensorParserConfigHistory[]) { + this.allSensorParserConfigHistory = allSensorParserConfigHistory; + } + + public getAll(): Observable<SensorParserConfigHistory[]> { + return Observable.create(observer => { + observer.next(this.allSensorParserConfigHistory); + observer.complete(); + }); + } +} + +class MockSensorParserConfigService extends SensorParserConfigService { + private sensorParserConfigs: SensorParserConfig[]; + + constructor(private http2: Http, @Inject(APP_CONFIG) private config2: IAppConfig) { + super(http2, config2); + } + + public setSensorParserConfigForTest(sensorParserConfigs: SensorParserConfig[]) { + this.sensorParserConfigs = sensorParserConfigs; + } + + public getAll(): Observable<SensorParserConfig[]> { + return Observable.create(observer => { + observer.next(this.sensorParserConfigs); + observer.complete(); + }); + } + + public deleteSensorParserConfigs(sensors: SensorParserConfig[]): Observable<{success: Array<string>, failure: Array<string>}> { + let result: {success: Array<string>, failure: Array<string>} = {success: [], failure: []}; + let observable = Observable.create((observer => { + for (let i = 0; i < sensors.length; i++) { + result.success.push(sensors[i].sensorTopic); + } + observer.next(result); + observer.complete(); + })); + return observable; + } +} + +class MockStormService extends StormService { + private topologyStatuses: TopologyStatus[]; + + constructor(private http2: Http, @Inject(APP_CONFIG) private config2: IAppConfig) { + super(http2, config2); + } + + public setTopologyStatusForTest(topologyStatuses: TopologyStatus[]) { + this.topologyStatuses = topologyStatuses; + } + + public pollGetAll(): Observable<TopologyStatus[]> { + return Observable.create(observer => { + observer.next(this.topologyStatuses); + observer.complete(); + }); + } + + public getAll(): Observable<TopologyStatus[]> { + return Observable.create(observer => { + observer.next(this.topologyStatuses); + observer.complete(); + }); + } +} + +class MockRouter { + events: Observable<Event> = Observable.create(observer => { + observer.next(new NavigationStart(1, '/sensors')); + observer.complete(); + }); + + navigateByUrl(url: string) { + } +} + +class MockMetronDialogBox { + + public showConfirmationMessage(message: string) { + return Observable.create(observer => { + observer.next(true); + observer.complete(); + }); + } +} + +describe('Component: SensorParserList', () => { + + let comp: SensorParserListComponent; + let fixture: ComponentFixture<SensorParserListComponent>; + let authenticationService: MockAuthenticationService; + let sensorParserConfigService: MockSensorParserConfigService; + let stormService: MockStormService; + let sensorParserConfigHistoryService: MockSensorParserConfigHistoryService; + let router: Router; + let metronAlerts: MetronAlerts; + let metronDialog: MetronDialogBox; + let dialogEl: DebugElement; + + beforeEach(async(() => { + + TestBed.configureTestingModule({ + imports: [SensorParserListModule], + providers: [ + {provide: Http}, + {provide: Location, useClass: SpyLocation}, + {provide: AuthenticationService, useClass: MockAuthenticationService}, + {provide: SensorParserConfigService, useClass: MockSensorParserConfigService}, + {provide: StormService, useClass: MockStormService}, + {provide: SensorParserConfigHistoryService, useClass: MockSensorParserConfigHistoryService}, + {provide: Router, useClass: MockRouter}, + {provide: MetronDialogBox, useClass: MockMetronDialogBox}, + {provide: APP_CONFIG, useValue: METRON_REST_CONFIG}, + MetronAlerts + ] + }).compileComponents() + .then(() => { + fixture = TestBed.createComponent(SensorParserListComponent); + comp = fixture.componentInstance; + authenticationService = fixture.debugElement.injector.get(AuthenticationService); + sensorParserConfigService = fixture.debugElement.injector.get(SensorParserConfigService); + stormService = fixture.debugElement.injector.get(StormService); + sensorParserConfigHistoryService = fixture.debugElement.injector.get(SensorParserConfigHistoryService); + router = fixture.debugElement.injector.get(Router); + metronAlerts = fixture.debugElement.injector.get(MetronAlerts); + metronDialog = fixture.debugElement.injector.get(MetronDialogBox); + dialogEl = fixture.debugElement.query(By.css('.primary')); + }); + + })); + + it('should create an instance', async(() => { + + let component: SensorParserListComponent = fixture.componentInstance; + expect(component).toBeDefined(); + fixture.destroy(); + + })); + + it('getSensors should call getStatus and poll status and all variables should be initialised', async(() => { + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfigHistory2 = new SensorParserConfigHistory(); + let sensorParserConfig1 = new SensorParserConfig(); + let sensorParserConfig2 = new SensorParserConfig(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfig2.sensorTopic = 'bro'; + sensorParserConfigHistory1.config = sensorParserConfig1; + sensorParserConfigHistory2.config = sensorParserConfig2; + + let sensorParserStatus1 = new TopologyStatus(); + let sensorParserStatus2 = new TopologyStatus(); + sensorParserStatus1.name = 'squid'; + sensorParserStatus1.status = 'KILLED'; + sensorParserStatus2.name = 'bro'; + sensorParserStatus2.status = 'KILLED'; + + sensorParserConfigHistoryService.setSensorParserConfigHistoryForTest([sensorParserConfigHistory1, sensorParserConfigHistory2]); + stormService.setTopologyStatusForTest([sensorParserStatus1, sensorParserStatus2]); + + let component: SensorParserListComponent = fixture.componentInstance; + + component.enableAutoRefresh = false; + + component.ngOnInit(); + + expect(component.sensors[0]).toEqual(sensorParserConfigHistory1); + expect(component.sensors[1]).toEqual(sensorParserConfigHistory2); + expect(component.sensorsStatus[0]).toEqual(Object.assign(new TopologyStatus(), sensorParserStatus1)); + expect(component.sensorsStatus[1]).toEqual(Object.assign(new TopologyStatus(), sensorParserStatus2)); + expect(component.selectedSensors).toEqual([]); + expect(component.count).toEqual(2); + + fixture.destroy(); + + })); + + it('getParserType should return the Type of Parser', async(() => { + + let component: SensorParserListComponent = fixture.componentInstance; + + let sensorParserConfig1 = new SensorParserConfig(); + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfig1.parserClassName = 'org.apache.metron.parsers.GrokParser'; + let sensorParserConfig2 = new SensorParserConfig(); + sensorParserConfig2.sensorTopic = 'bro'; + sensorParserConfig2.parserClassName = 'org.apache.metron.parsers.bro.BasicBroParser'; + + expect(component.getParserType(sensorParserConfig1)).toEqual('Grok'); + expect(component.getParserType(sensorParserConfig2)).toEqual('Bro'); + + fixture.destroy(); + + })); + + it('navigateToSensorEdit should set selected sensor and change url', async(() => { + let event = new Event('mouse'); + event.stopPropagation = jasmine.createSpy('stopPropagation'); + + spyOn(router, 'navigateByUrl'); + + let component: SensorParserListComponent = fixture.componentInstance; + + let sensorParserConfig1 = new SensorParserConfig(); + sensorParserConfig1.sensorTopic = 'squid'; + component.navigateToSensorEdit(sensorParserConfig1, event); + + let expectStr = router.navigateByUrl['calls'].argsFor(0); + expect(expectStr).toEqual(['/sensors(dialog:sensors-config/squid)']); + + expect(event.stopPropagation).toHaveBeenCalled(); + + fixture.destroy(); + })); + + it('addAddSensor should change the URL', async(() => { + spyOn(router, 'navigateByUrl'); + + let component: SensorParserListComponent = fixture.componentInstance; + + component.addAddSensor(); + + let expectStr = router.navigateByUrl['calls'].argsFor(0); + expect(expectStr).toEqual(['/sensors(dialog:sensors-config/new)']); + + fixture.destroy(); + })); + + + it('onRowSelected should add add/remove items from the selected stack', async(() => { + + let component: SensorParserListComponent = fixture.componentInstance; + let event = {target: {checked: true}}; + + let sensorParserConfigHistory = new SensorParserConfigHistory(); + let sensorParserConfig = new SensorParserConfig(); + + sensorParserConfig.sensorTopic = 'squid'; + sensorParserConfigHistory.config = sensorParserConfig; + + component.onRowSelected(sensorParserConfigHistory, event); + + expect(component.selectedSensors[0]).toEqual(sensorParserConfigHistory); + + event = {target: {checked: false}}; + + component.onRowSelected(sensorParserConfigHistory, event); + expect(component.selectedSensors).toEqual([]); + + fixture.destroy(); + })); + + it('onSelectDeselectAll should populate items into selected stack', async(() => { + + let component: SensorParserListComponent = fixture.componentInstance; + + let sensorParserConfig1 = new SensorParserConfig(); + let sensorParserConfig2 = new SensorParserConfig(); + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfigHistory2 = new SensorParserConfigHistory(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfigHistory1.config = sensorParserConfig1; + sensorParserConfig2.sensorTopic = 'bro'; + sensorParserConfigHistory2.config = sensorParserConfig2; + + component.sensors.push(sensorParserConfigHistory1); + component.sensors.push(sensorParserConfigHistory2); + + let event = {target: {checked: true}}; + + component.onSelectDeselectAll(event); + + expect(component.selectedSensors).toEqual([sensorParserConfigHistory1, sensorParserConfigHistory2]); + + event = {target: {checked: false}}; + + component.onSelectDeselectAll(event); + + expect(component.selectedSensors).toEqual([]); + + fixture.destroy(); + })); + + it('onSensorRowSelect should change the url and updated the selected items stack', async(() => { + + let sensorParserConfig1 = new SensorParserConfig(); + sensorParserConfig1.sensorTopic = 'squid'; + + let component: SensorParserListComponent = fixture.componentInstance; + let event = {target: {type: 'div', parentElement: {firstChild: {type: 'div'}}}}; + + sensorParserConfigService.setSeletedSensor(sensorParserConfig1); + component.onSensorRowSelect(sensorParserConfig1, event); + + expect(sensorParserConfigService.getSelectedSensor()).toEqual(null); + + component.onSensorRowSelect(sensorParserConfig1, event); + + expect(sensorParserConfigService.getSelectedSensor()).toEqual(sensorParserConfig1); + + sensorParserConfigService.setSeletedSensor(sensorParserConfig1); + event = {target: {type: 'checkbox', parentElement: {firstChild: {type: 'div'}}}}; + + component.onSensorRowSelect(sensorParserConfig1, event); + + expect(sensorParserConfigService.getSelectedSensor()).toEqual(sensorParserConfig1); + + fixture.destroy(); + })); + + it('onSensorRowSelect should change the url and updated the selected items stack', async(() => { + + let component: SensorParserListComponent = fixture.componentInstance; + + let sensorParserConfigHistory = new SensorParserConfigHistory(); + let sensorParserConfig = new SensorParserConfig(); + + sensorParserConfig.sensorTopic = 'squid'; + sensorParserConfigHistory.config = sensorParserConfig; + + component.toggleStartStopInProgress(sensorParserConfigHistory); + expect(sensorParserConfig['startStopInProgress']).toEqual(true); + + component.toggleStartStopInProgress(sensorParserConfigHistory); + expect(sensorParserConfig['startStopInProgress']).toEqual(false); + })); + + it('onDeleteSensor should call the appropriate url', async(() => { + + spyOn(metronAlerts, 'showSuccessMessage'); + spyOn(metronDialog, 'showConfirmationMessage').and.callThrough(); + + let event = new Event('mouse'); + event.stopPropagation = jasmine.createSpy('stopPropagation'); + + let component: SensorParserListComponent = fixture.componentInstance; + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfigHistory2 = new SensorParserConfigHistory(); + let sensorParserConfig1 = new SensorParserConfig(); + let sensorParserConfig2 = new SensorParserConfig(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfig2.sensorTopic = 'bro'; + sensorParserConfigHistory1.config = sensorParserConfig1; + sensorParserConfigHistory2.config = sensorParserConfig2; + + component.selectedSensors.push(sensorParserConfigHistory1); + component.selectedSensors.push(sensorParserConfigHistory2); + + component.onDeleteSensor(); + + expect(metronAlerts.showSuccessMessage).toHaveBeenCalled(); + + component.deleteSensor(event, [sensorParserConfigHistory1.config]); + + expect(metronDialog.showConfirmationMessage).toHaveBeenCalled(); + expect(metronDialog.showConfirmationMessage['calls'].count()).toEqual(2); + expect(metronDialog.showConfirmationMessage['calls'].count()).toEqual(2); + expect(metronDialog.showConfirmationMessage['calls'].all()[0].args).toEqual(['Are you sure you want to delete sensor(s) squid, bro ?']); + expect(metronDialog.showConfirmationMessage['calls'].all()[1].args).toEqual(['Are you sure you want to delete sensor(s) squid ?']); + + expect(event.stopPropagation).toHaveBeenCalled(); + + fixture.destroy(); + + })); + + it('onStopSensor should call the appropriate url', async(() => { + let event = new Event('mouse'); + event.stopPropagation = jasmine.createSpy('stopPropagation'); + + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfig1 = new SensorParserConfig(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfigHistory1.config = sensorParserConfig1; + + let observableToReturn = Observable.create(observer => { + observer.next({status: 'success', message: 'Some Message'}); + observer.complete(); + }); + + spyOn(metronAlerts, 'showSuccessMessage'); + spyOn(stormService, 'stopParser').and.returnValue(observableToReturn); + + let component: SensorParserListComponent = fixture.componentInstance; + + component.onStopSensor(sensorParserConfigHistory1, event); + + expect(stormService.stopParser).toHaveBeenCalled(); + expect(event.stopPropagation).toHaveBeenCalled(); + expect(metronAlerts.showSuccessMessage).toHaveBeenCalled(); + + fixture.destroy(); + })); + + it('onStartSensor should call the appropriate url', async(() => { + let event = new Event('mouse'); + event.stopPropagation = jasmine.createSpy('stopPropagation'); + + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfig1 = new SensorParserConfig(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfigHistory1.config = sensorParserConfig1; + + let observableToReturn = Observable.create(observer => { + observer.next({status: 'success', message: 'Some Message'}); + observer.complete(); + }); + + spyOn(metronAlerts, 'showSuccessMessage'); + spyOn(stormService, 'startParser').and.returnValue(observableToReturn); + + let component: SensorParserListComponent = fixture.componentInstance; + + component.onStartSensor(sensorParserConfigHistory1, event); + + expect(stormService.startParser).toHaveBeenCalled(); + expect(event.stopPropagation).toHaveBeenCalled(); + expect(metronAlerts.showSuccessMessage).toHaveBeenCalled(); + + fixture.destroy(); + })); + + it('onEnableSensor should call the appropriate url', async(() => { + let event = new Event('mouse'); + event.stopPropagation = jasmine.createSpy('stopPropagation'); + + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfig1 = new SensorParserConfig(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfigHistory1.config = sensorParserConfig1; + + let observableToReturn = Observable.create(observer => { + observer.next({status: 'success', message: 'Some Message'}); + observer.complete(); + }); + + spyOn(metronAlerts, 'showSuccessMessage'); + spyOn(stormService, 'activateParser').and.returnValue(observableToReturn); + + let component: SensorParserListComponent = fixture.componentInstance; + + component.onEnableSensor(sensorParserConfigHistory1, event); + + expect(stormService.activateParser).toHaveBeenCalled(); + expect(event.stopPropagation).toHaveBeenCalled(); + expect(metronAlerts.showSuccessMessage).toHaveBeenCalled(); + + fixture.destroy(); + })); + + it('onDisableSensor should call the appropriate url', async(() => { + let event = new Event('mouse'); + event.stopPropagation = jasmine.createSpy('stopPropagation'); + + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfig1 = new SensorParserConfig(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfigHistory1.config = sensorParserConfig1; + + let observableToReturn = Observable.create(observer => { + observer.next({status: 'success', message: 'Some Message'}); + observer.complete(); + }); + + spyOn(metronAlerts, 'showSuccessMessage'); + spyOn(stormService, 'deactivateParser').and.returnValue(observableToReturn); + + let component: SensorParserListComponent = fixture.componentInstance; + + component.onDisableSensor(sensorParserConfigHistory1, event); + + expect(stormService.deactivateParser).toHaveBeenCalled(); + expect(event.stopPropagation).toHaveBeenCalled(); + expect(metronAlerts.showSuccessMessage).toHaveBeenCalled(); + + fixture.destroy(); + })); + + it('onStartSensors/onStopSensors should call start on all sensors that have status != ' + + 'Running and status != Running respectively', async(() => { + let component: SensorParserListComponent = fixture.componentInstance; + + spyOn(component, 'onStartSensor'); + spyOn(component, 'onStopSensor'); + spyOn(component, 'onDisableSensor'); + spyOn(component, 'onEnableSensor'); + + let sensorParserConfigHistory1 = new SensorParserConfigHistory(); + let sensorParserConfigHistory2 = new SensorParserConfigHistory(); + let sensorParserConfigHistory3 = new SensorParserConfigHistory(); + let sensorParserConfigHistory4 = new SensorParserConfigHistory(); + let sensorParserConfigHistory5 = new SensorParserConfigHistory(); + let sensorParserConfigHistory6 = new SensorParserConfigHistory(); + let sensorParserConfigHistory7 = new SensorParserConfigHistory(); + + let sensorParserConfig1 = new SensorParserConfig(); + let sensorParserConfig2 = new SensorParserConfig(); + let sensorParserConfig3 = new SensorParserConfig(); + let sensorParserConfig4 = new SensorParserConfig(); + let sensorParserConfig5 = new SensorParserConfig(); + let sensorParserConfig6 = new SensorParserConfig(); + let sensorParserConfig7 = new SensorParserConfig(); + + sensorParserConfig1.sensorTopic = 'squid'; + sensorParserConfigHistory1['status'] = 'Running'; + sensorParserConfigHistory1.config = sensorParserConfig1; + + sensorParserConfig2.sensorTopic = 'bro'; + sensorParserConfigHistory2['status'] = 'Stopped'; + sensorParserConfigHistory2.config = sensorParserConfig2; + + sensorParserConfig3.sensorTopic = 'test'; + sensorParserConfigHistory3['status'] = 'Stopped'; + sensorParserConfigHistory3.config = sensorParserConfig3; + + sensorParserConfig4.sensorTopic = 'test1'; + sensorParserConfigHistory4['status'] = 'Stopped'; + sensorParserConfigHistory4.config = sensorParserConfig4; + + sensorParserConfig5.sensorTopic = 'test2'; + sensorParserConfigHistory5['status'] = 'Running'; + sensorParserConfigHistory5.config = sensorParserConfig5; + + sensorParserConfig6.sensorTopic = 'test2'; + sensorParserConfigHistory6['status'] = 'Disabled'; + sensorParserConfigHistory6.config = sensorParserConfig6; + + sensorParserConfig7.sensorTopic = 'test3'; + sensorParserConfigHistory7['status'] = 'Disabled'; + sensorParserConfigHistory7.config = sensorParserConfig7; + + component.selectedSensors = [sensorParserConfigHistory1, sensorParserConfigHistory2, sensorParserConfigHistory3, + sensorParserConfigHistory4, sensorParserConfigHistory5, sensorParserConfigHistory6, sensorParserConfigHistory7]; + + component.onStartSensors(); + expect(component.onStartSensor['calls'].count()).toEqual(3); + + component.onStopSensors(); + expect(component.onStopSensor['calls'].count()).toEqual(4); + + component.onDisableSensors(); + expect(component.onDisableSensor['calls'].count()).toEqual(2); + + component.onEnableSensors(); + expect(component.onEnableSensor['calls'].count()).toEqual(2); + + fixture.destroy(); + })); + + it('sort', async(() => { + let component: SensorParserListComponent = fixture.componentInstance; + + component.sensors = [ + Object.assign(new SensorParserConfigHistory(), { + 'config': { + 'parserClassName': 'org.apache.metron.parsers.GrokParser', + 'sensorTopic': 'abc', + }, + 'createdBy': 'raghu', + 'modifiedBy': 'abc', + 'createdDate': '2016-11-25 09:09:12', + 'modifiedByDate': '2016-11-25 09:09:12' + }), + Object.assign(new SensorParserConfigHistory(), { + 'config': { + 'parserClassName': 'org.apache.metron.parsers.Bro', + 'sensorTopic': 'plm', + }, + 'createdBy': 'raghu', + 'modifiedBy': 'plm', + 'createdDate': '2016-11-25 12:39:21', + 'modifiedByDate': '2016-11-25 12:39:21' + }), + Object.assign(new SensorParserConfigHistory(), { + 'config': { + 'parserClassName': 'org.apache.metron.parsers.GrokParser', + 'sensorTopic': 'xyz', + }, + 'createdBy': 'raghu', + 'modifiedBy': 'xyz', + 'createdDate': '2016-11-25 12:44:03', + 'modifiedByDate': '2016-11-25 12:44:03' + }) + ]; + + component.onSort({sortBy: 'sensorTopic', sortOrder: Sort.ASC}); + expect(component.sensors[0].config.sensorTopic).toEqual('abc'); + expect(component.sensors[1].config.sensorTopic).toEqual('plm'); + expect(component.sensors[2].config.sensorTopic).toEqual('xyz'); + + component.onSort({sortBy: 'sensorTopic', sortOrder: Sort.DSC}); + expect(component.sensors[0].config.sensorTopic).toEqual('xyz'); + expect(component.sensors[1].config.sensorTopic).toEqual('plm'); + expect(component.sensors[2].config.sensorTopic).toEqual('abc'); + + component.onSort({sortBy: 'parserClassName', sortOrder: Sort.ASC}); + expect(component.sensors[0].config.parserClassName).toEqual('org.apache.metron.parsers.Bro'); + expect(component.sensors[1].config.parserClassName).toEqual('org.apache.metron.parsers.GrokParser'); + expect(component.sensors[2].config.parserClassName).toEqual('org.apache.metron.parsers.GrokParser'); + + component.onSort({sortBy: 'parserClassName', sortOrder: Sort.DSC}); + expect(component.sensors[0].config.parserClassName).toEqual('org.apache.metron.parsers.GrokParser'); + expect(component.sensors[1].config.parserClassName).toEqual('org.apache.metron.parsers.GrokParser'); + expect(component.sensors[2].config.parserClassName).toEqual('org.apache.metron.parsers.Bro'); + + component.onSort({sortBy: 'modifiedBy', sortOrder: Sort.ASC}); + expect(component.sensors[0].modifiedBy).toEqual('abc'); + expect(component.sensors[1].modifiedBy).toEqual('plm'); + expect(component.sensors[2].modifiedBy).toEqual('xyz'); + + component.onSort({sortBy: 'modifiedBy', sortOrder: Sort.DSC}); + expect(component.sensors[0].modifiedBy).toEqual('xyz'); + expect(component.sensors[1].modifiedBy).toEqual('plm'); + expect(component.sensors[2].modifiedBy).toEqual('abc'); + + })); + + it('sort', async(() => { + let component: SensorParserListComponent = fixture.componentInstance; + + component.sensors = [ + Object.assign(new SensorParserConfigHistory(), { + 'config': { + 'parserClassName': 'org.apache.metron.parsers.GrokParser', + 'sensorTopic': 'abc', + }, + 'createdBy': 'raghu', + 'modifiedBy': 'abc', + 'createdDate': '2016-11-25 09:09:12', + 'modifiedByDate': '2016-11-25 09:09:12' + })]; + + component.sensorsStatus = [ + Object.assign(new TopologyStatus(), { + 'name': 'abc', + 'status': 'ACTIVE', + 'latency': '10', + 'throughput': '23' + }) + ]; + + component.updateSensorStatus(); + expect(component.sensors[0]['status']).toEqual('Running'); + expect(component.sensors[0]['latency']).toEqual('10s'); + expect(component.sensors[0]['throughput']).toEqual('23kb/s'); + + component.sensorsStatus[0].status = 'KILLED'; + component.updateSensorStatus(); + expect(component.sensors[0]['status']).toEqual('Stopped'); + expect(component.sensors[0]['latency']).toEqual('-'); + expect(component.sensors[0]['throughput']).toEqual('-'); + + component.sensorsStatus[0].status = 'INACTIVE'; + component.updateSensorStatus(); + expect(component.sensors[0]['status']).toEqual('Disabled'); + expect(component.sensors[0]['latency']).toEqual('-'); + expect(component.sensors[0]['throughput']).toEqual('-'); + + component.sensorsStatus = []; + component.updateSensorStatus(); + expect(component.sensors[0]['status']).toEqual('Stopped'); + expect(component.sensors[0]['latency']).toEqual('-'); + expect(component.sensors[0]['throughput']).toEqual('-'); + + })); + +});
http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts new file mode 100644 index 0000000..ccc8aca --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.component.ts @@ -0,0 +1,368 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {Component, OnInit, ViewChild} from '@angular/core'; +import {Router, NavigationStart} from '@angular/router'; +import {SensorParserConfig} from '../../model/sensor-parser-config'; +import {SensorParserConfigService} from '../../service/sensor-parser-config.service'; +import {MetronAlerts} from '../../shared/metron-alerts'; +import {MetronDialogBox} from '../../shared/metron-dialog-box'; +import {Sort} from '../../util/enums'; +import {SortEvent} from '../../shared/metron-table/metron-table.directive'; +import {StormService} from '../../service/storm.service'; +import {TopologyStatus} from '../../model/topology-status'; +import {SensorParserConfigHistory} from '../../model/sensor-parser-config-history'; +import {SensorParserConfigHistoryService} from '../../service/sensor-parser-config-history.service'; + +@Component({ + selector: 'metron-config-sensor-parser-list', + templateUrl: 'sensor-parser-list.component.html', + styleUrls: ['sensor-parser-list.component.scss'] +}) +export class SensorParserListComponent implements OnInit { + + componentName: string = 'Sensors'; + @ViewChild('table') table; + + count: number = 0; + sensors: SensorParserConfigHistory[] = []; + sensorsStatus: TopologyStatus[] = []; + selectedSensors: SensorParserConfigHistory[] = []; + enableAutoRefresh: boolean = true; + + constructor(private sensorParserConfigService: SensorParserConfigService, + private sensorParserConfigHistoryService: SensorParserConfigHistoryService, + private stormService: StormService, + private router: Router, + private metronAlerts: MetronAlerts, + private metronDialogBox: MetronDialogBox) { + router.events.subscribe(event => { + if (event instanceof NavigationStart && event.url === '/sensors') { + this.onNavigationStart(); + } + }); + } + + getSensors(justOnce: boolean) { + this.sensorParserConfigHistoryService.getAll().subscribe( + (results: SensorParserConfigHistory[]) => { + this.sensors = results; + this.selectedSensors = []; + this.count = this.sensors.length; + if (!justOnce) { + this.getStatus(); + this.pollStatus(); + } else { + this.getStatus(); + } + + } + ); + } + + onSort($event: SortEvent) { + switch ($event.sortBy) { + case 'sensorTopic': + this.sensors.sort((obj1: SensorParserConfigHistory, obj2: SensorParserConfigHistory) => { + if ($event.sortOrder === Sort.ASC) { + return obj1.config[$event.sortBy].localeCompare(obj2.config[$event.sortBy]); + } + return obj2.config[$event.sortBy].localeCompare(obj1.config[$event.sortBy]); + }); + break; + case 'parserClassName': + this.sensors.sort((obj1: SensorParserConfigHistory, obj2: SensorParserConfigHistory) => { + if ($event.sortOrder === Sort.ASC) { + return this.getParserType(obj1.config).localeCompare(this.getParserType(obj2.config)); + } + return this.getParserType(obj2.config).localeCompare(this.getParserType(obj1.config)); + }); + break; + case 'status': + case 'modifiedBy': + case 'modifiedByDate': + case 'latency': + case 'throughput': + this.sensors.sort((obj1: SensorParserConfigHistory, obj2: SensorParserConfigHistory) => { + if (!obj1[$event.sortBy] || !obj1[$event.sortBy]) { + return 0; + } + if ($event.sortOrder === Sort.ASC) { + return obj1[$event.sortBy].localeCompare(obj2[$event.sortBy]); + } + + return obj2[$event.sortBy].localeCompare(obj1[$event.sortBy]); + }); + break; + } + } + + private pollStatus() { + this.stormService.pollGetAll().subscribe( + (results: TopologyStatus[]) => { + this.sensorsStatus = results; + this.updateSensorStatus(); + }, + error => { + this.updateSensorStatus(); + } + ); + } + + private getStatus() { + this.stormService.getAll().subscribe( + (results: TopologyStatus[]) => { + this.sensorsStatus = results; + this.updateSensorStatus(); + }, + error => { + this.updateSensorStatus(); + } + ); + } + + updateSensorStatus() { + for (let sensor of this.sensors) { + + let status: TopologyStatus = this.sensorsStatus.find(status => { + return status.name === sensor.config.sensorTopic; + }); + + if (status) { + if (status.status === 'ACTIVE') { + sensor['status'] = 'Running'; + } + if (status.status === 'KILLED') { + sensor['status'] = 'Stopped'; + } + if (status.status === 'INACTIVE') { + sensor['status'] = 'Disabled'; + } + } else { + sensor['status'] = 'Stopped'; + } + + sensor['latency'] = status && status.status === 'ACTIVE' ? (status.latency + 's') : '-'; + sensor['throughput'] = status && status.status === 'ACTIVE' ? (Math.round(status.throughput * 100) / 100) + 'kb/s' : '-'; + } + } + + getParserType(sensor: SensorParserConfig): string { + let items = sensor.parserClassName.split('.'); + return items[items.length - 1].replace('Basic', '').replace('Parser', ''); + } + + ngOnInit() { + this.getSensors(false); + this.sensorParserConfigService.dataChanged$.subscribe( + data => { + this.getSensors(true); + } + ); + } + + addAddSensor() { + this.router.navigateByUrl('/sensors(dialog:sensors-config/new)'); + } + + navigateToSensorEdit(selectedSensor: SensorParserConfig, event) { + this.sensorParserConfigService.setSeletedSensor(selectedSensor); + this.router.navigateByUrl('/sensors(dialog:sensors-config/' + selectedSensor.sensorTopic + ')'); + event.stopPropagation(); + } + + onRowSelected(sensor: SensorParserConfigHistory, $event) { + if ($event.target.checked) { + this.selectedSensors.push(sensor); + } else { + this.selectedSensors.splice(this.selectedSensors.indexOf(sensor), 1); + } + } + + onSelectDeselectAll($event) { + let checkBoxes = this.table.nativeElement.querySelectorAll('tr td:last-child input[type="checkbox"]'); + + for (let ele of checkBoxes) { + ele.checked = $event.target.checked; + } + + if ($event.target.checked) { + this.selectedSensors = this.sensors.slice(0).map((sensorParserInfo: SensorParserConfigHistory) => sensorParserInfo); + } else { + this.selectedSensors = []; + } + } + + onSensorRowSelect(sensor: SensorParserConfig, $event) { + if ($event.target.type !== 'checkbox' && $event.target.parentElement.firstChild.type !== 'checkbox') { + + if (this.sensorParserConfigService.getSelectedSensor() === sensor) { + this.sensorParserConfigService.setSeletedSensor(null); + this.router.navigateByUrl('/sensors'); + return; + } + + this.sensorParserConfigService.setSeletedSensor(sensor); + this.router.navigateByUrl('/sensors(dialog:sensors-readonly/' + sensor.sensorTopic + ')'); + } + } + + deleteSensor($event, selectedSensorsToDelete: SensorParserConfig[]) { + if ($event) { + $event.stopPropagation(); + } + + let sensorNames = selectedSensorsToDelete.map(sensor => { return sensor.sensorTopic; }); + let confirmationsMsg = 'Are you sure you want to delete sensor(s) ' + sensorNames.join(', ') + ' ?'; + + this.metronDialogBox.showConfirmationMessage(confirmationsMsg).subscribe(result => { + if (result) { + this.sensorParserConfigService.deleteSensorParserConfigs(selectedSensorsToDelete) + .subscribe((deleteResult: {success: Array<string>, failure: Array<string>}) => { + if (deleteResult.success.length > 0) { + this.metronAlerts.showSuccessMessage('Deleted sensors: ' + deleteResult.success.join(', ')); + } + + if (deleteResult.failure.length > 0) { + this.metronAlerts.showErrorMessage('Unable to deleted sensors: ' + deleteResult.failure.join(', ')); + } + }); + } + }); + } + + onDeleteSensor() { + let selectedSensorsToDelete = this.selectedSensors.map(info => { + return info.config; + }); + this.deleteSensor(null, selectedSensorsToDelete); + } + + onStopSensors() { + for (let sensor of this.selectedSensors) { + if (sensor['status'] === 'Running' || sensor['status'] === 'Disabled') { + this.onStopSensor(sensor, null); + } + } + } + + onStopSensor(sensor: SensorParserConfigHistory, event) { + this.toggleStartStopInProgress(sensor); + + this.stormService.stopParser(sensor.config.sensorTopic).subscribe(result => { + this.metronAlerts.showSuccessMessage('Stopped sensor ' + sensor.config.sensorTopic); + this.toggleStartStopInProgress(sensor); + }, + error => { + this.metronAlerts.showErrorMessage('Unable to stop sensor ' + sensor.config.sensorTopic); + this.toggleStartStopInProgress(sensor); + }); + + if (event !== null) { + event.stopPropagation(); + } + } + + onStartSensors() { + for (let sensor of this.selectedSensors) { + if (sensor['status'] === 'Stopped') { + this.onStartSensor(sensor, null); + } + } + } + + onStartSensor(sensor: SensorParserConfigHistory, event) { + this.toggleStartStopInProgress(sensor); + + this.stormService.startParser(sensor.config.sensorTopic).subscribe(result => { + if (result['status'] === 'ERROR') { + this.metronAlerts.showErrorMessage('Unable to start sensor ' + sensor.config.sensorTopic + ': ' + result['message']); + } else { + this.metronAlerts.showSuccessMessage('Started sensor ' + sensor.config.sensorTopic); + } + + this.toggleStartStopInProgress(sensor); + }, + error => { + this.metronAlerts.showErrorMessage('Unable to start sensor ' + sensor.config.sensorTopic); + this.toggleStartStopInProgress(sensor); + }); + + if (event !== null) { + event.stopPropagation(); + } + } + + onDisableSensors() { + for (let sensor of this.selectedSensors) { + if (sensor['status'] === 'Running') { + this.onDisableSensor(sensor, null); + } + } + } + + onDisableSensor(sensor: SensorParserConfigHistory, event) { + this.toggleStartStopInProgress(sensor); + + this.stormService.deactivateParser(sensor.config.sensorTopic).subscribe(result => { + this.metronAlerts.showSuccessMessage('Disabled sensor ' + sensor.config.sensorTopic); + this.toggleStartStopInProgress(sensor); + }, + error => { + this.metronAlerts.showErrorMessage('Unable to disable sensor ' + sensor.config.sensorTopic); + this.toggleStartStopInProgress(sensor); + }); + + if (event !== null) { + event.stopPropagation(); + } + } + + onEnableSensors() { + for (let sensor of this.selectedSensors) { + if (sensor['status'] === 'Disabled') { + this.onEnableSensor(sensor, null); + } + } + } + + onEnableSensor(sensor: SensorParserConfigHistory, event) { + this.toggleStartStopInProgress(sensor); + + this.stormService.activateParser(sensor.config.sensorTopic).subscribe(result => { + this.metronAlerts.showSuccessMessage('Enabled sensor ' + sensor.config.sensorTopic); + this.toggleStartStopInProgress(sensor); + }, + error => { + this.metronAlerts.showErrorMessage('Unable to enabled sensor ' + sensor.config.sensorTopic); + this.toggleStartStopInProgress(sensor); + }); + + if (event != null) { + event.stopPropagation(); + } + } + + toggleStartStopInProgress(sensor: SensorParserConfigHistory) { + sensor.config['startStopInProgress'] = !sensor.config['startStopInProgress']; + } + + onNavigationStart() { + this.sensorParserConfigService.setSeletedSensor(null); + this.selectedSensors = []; + } +} http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.module.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.module.ts new file mode 100644 index 0000000..d057623 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.module.ts @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { NgModule } from '@angular/core'; +import {routing} from './sensor-parser-list.routing'; +import {SensorParserListComponent} from './sensor-parser-list.component'; +import {SharedModule} from '../../shared/shared.module'; +import {APP_CONFIG, METRON_REST_CONFIG} from '../../app.config'; +import {MetronTableModule} from '../../shared/metron-table/metron-table.module'; + +@NgModule ({ + imports: [ routing, SharedModule, MetronTableModule ], + declarations: [ SensorParserListComponent ], + providers: [{ provide: APP_CONFIG, useValue: METRON_REST_CONFIG }] +}) +export class SensorParserListModule { } http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.routing.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.routing.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.routing.ts new file mode 100644 index 0000000..4910aa5 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-list/sensor-parser-list.routing.ts @@ -0,0 +1,25 @@ +/** + * 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 { ModuleWithProviders } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import {SensorParserListComponent} from './sensor-parser-list.component'; +import {AuthGuard} from '../../shared/auth-guard'; + +export const routing: ModuleWithProviders = RouterModule.forChild([ + { path: '', component: SensorParserListComponent, canActivate: [AuthGuard]} +]); http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.html b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.html new file mode 100644 index 0000000..291327f --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.html @@ -0,0 +1,49 @@ +<!-- + Licensed to the Apache Software + Foundation (ASF) under one or more contributor license agreements. See the + NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to You under the Apache License, + Version 2.0 (the "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + OR CONDITIONS OF ANY KIND, either express or implied. See the License for + the specific language governing permissions and limitations under the License. + --> +<div class="metron-slider-pane-edit fill load-left-to-right dialog2x"> + + <div class="form-group"> + <div class="form-title">Configure Raw JSON</div> + <i class="fa fa-times pull-right close-button" aria-hidden="true" (click)="onCancel()"></i> + </div> + + + <form role="form" class="enrichments-form"> + + <div class="form-group"> + <label attr.for="newSensorParserConfig">SENSOR PARSER CONFIG</label> + <metron-config-ace-editor [(ngModel)]="newSensorParserConfig" [ngModelOptions]="{standalone: true}" [type]="'JSON'" [placeHolder]="'Enter Sensor Parser Config'"> </metron-config-ace-editor> + </div> + <div class="form-group"> + <label attr.for="newSensorEnrichmentConfig">SENSOR ENRICHMENT CONFIG</label> + <metron-config-ace-editor [(ngModel)]="newSensorEnrichmentConfig" [ngModelOptions]="{standalone: true}" [type]="'JSON'" [placeHolder]="'Enter Sensor Enrichment Config'"> </metron-config-ace-editor> + </div> + <div class="form-group"> + <label attr.for="newIndexingConfigurations">INDEXING CONFIGURATIONS</label> + <metron-config-ace-editor [(ngModel)]="newIndexingConfigurations" [ngModelOptions]="{standalone: true}" [type]="'JSON'" [placeHolder]="'Enter Indexing Configurations'"> </metron-config-ace-editor> + </div> + + <div class="form-group"> + <div class="form-seperator-edit"></div> + <div class="button-row"> + <button type="submit" class="btn form-enable-disable-button" (click)="onSave()">SAVE</button> + <button class="btn form-enable-disable-button" (click)="onCancel()" >CANCEL</button> + </div> + </div> + + </form> + +</div> http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.scss ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.scss b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.scss new file mode 100644 index 0000000..3a9f5e3 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.scss @@ -0,0 +1,36 @@ +/** + * 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. + */ +textarea +{ + height: auto; +} + +.form-group +{ + padding-left: 25px; + padding-right: 20px; + padding-bottom: 10px; +} + +.button-row { + padding-top: 10px; +} + +.form-enable-disable-button { + width: 16%; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.spec.ts new file mode 100644 index 0000000..f680f10 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.spec.ts @@ -0,0 +1,191 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, TestBed, ComponentFixture} from '@angular/core/testing'; +import {SensorRawJsonComponent} from './sensor-raw-json.component'; +import {SharedModule} from '../../shared/shared.module'; +import {SimpleChanges, SimpleChange} from '@angular/core'; +import {SensorParserConfig} from '../../model/sensor-parser-config'; +import {SensorEnrichmentConfig, EnrichmentConfig, ThreatIntelConfig} from '../../model/sensor-enrichment-config'; +import {SensorRawJsonModule} from './sensor-raw-json.module'; +import {IndexingConfigurations} from '../../model/sensor-indexing-config'; +import '../../rxjs-operators'; + +describe('Component: SensorRawJsonComponent', () => { + + let fixture: ComponentFixture<SensorRawJsonComponent>; + let component: SensorRawJsonComponent; + let sensorParserConfigString = '{"parserClassName":"org.apache.metron.parsers.bro.BasicBroParser","sensorTopic":"bro",' + + '"parserConfig": {},"fieldTransformations":[]}'; + let sensorParserConfig: SensorParserConfig = new SensorParserConfig(); + sensorParserConfig.sensorTopic = 'bro'; + sensorParserConfig.parserClassName = 'org.apache.metron.parsers.bro.BasicBroParser'; + sensorParserConfig.parserConfig = {}; + + let sensorParserConfigWithClassNameString = `{"parserClassName":"org.apache.metron.parsers.bro.BasicBroParser","sensorTopic":"bro", + "parserConfig": {},"fieldTransformations":[], "writerClassName": "org.example.writerClassName", + "errorWriterClassName": "org.example.errorWriterClassName", + "filterClassName": "org.example.filterClassName", "invalidWriterClassName": "org.example.invalidWriterClassName"}`; + let sensorParserConfigWithClassName = Object.assign(new SensorParserConfig(), sensorParserConfig); + sensorParserConfigWithClassName.writerClassName = 'org.example.writerClassName'; + sensorParserConfigWithClassName.errorWriterClassName = 'org.example.errorWriterClassName'; + sensorParserConfigWithClassName.filterClassName = 'org.example.filterClassName'; + sensorParserConfigWithClassName.invalidWriterClassName = 'org.example.invalidWriterClassName'; + + let sensorEnrichmentConfigString = '{"enrichment" : {"fieldMap": ' + + '{"geo": ["ip_dst_addr", "ip_src_addr"],"host": ["host"]}},"threatIntel": {"fieldMap": {"hbaseThreatIntel":' + + ' ["ip_src_addr", "ip_dst_addr"]},"fieldToTypeMap": {"ip_src_addr" : ["malicious_ip"],"ip_dst_addr" : ["malicious_ip"]}}}'; + let sensorEnrichmentConfig = new SensorEnrichmentConfig(); + sensorEnrichmentConfig.enrichment = Object.assign(new EnrichmentConfig(), { + 'fieldMap': { + 'geo': ['ip_dst_addr', 'ip_src_addr'], + 'host': ['host'] + } + }); + sensorEnrichmentConfig.threatIntel = Object.assign(new ThreatIntelConfig(), { + 'fieldMap': { + 'hbaseThreatIntel': ['ip_src_addr', 'ip_dst_addr'] + }, + 'fieldToTypeMap': { + 'ip_src_addr' : ['malicious_ip'], + 'ip_dst_addr' : ['malicious_ip'] + } + }); + + let sensorEnrichmentConfigWithConfigString = `{"configuration": "some-configuration", + "enrichment" : {"fieldMap": {"geo": ["ip_dst_addr", "ip_src_addr"],"host": ["host"]}}, + "threatIntel": {"fieldMap": {"hbaseThreatIntel":["ip_src_addr", "ip_dst_addr"]}, + "fieldToTypeMap": {"ip_src_addr" : ["malicious_ip"],"ip_dst_addr" : ["malicious_ip"]}}}`; + let sensorEnrichmentConfigWithConfig = Object.assign(new SensorEnrichmentConfig(), sensorEnrichmentConfig); + sensorEnrichmentConfigWithConfig.configuration = 'some-configuration'; + + let sensorIndexingConfigString = `{"hdfs": {"index": "bro","batchSize": 5,"enabled":true}, + "elasticsearch": {"index": "bro","batchSize": 5,"enabled":true}, + "solr": {"index": "bro","batchSize": 5,"enabled":true}}`; + let sensorIndexingConfig = new IndexingConfigurations(); + sensorIndexingConfig.hdfs.index = 'bro'; + sensorIndexingConfig.hdfs.batchSize = 5; + sensorIndexingConfig.hdfs.enabled = true; + sensorIndexingConfig.elasticsearch.index = 'bro'; + sensorIndexingConfig.elasticsearch.batchSize = 5; + sensorIndexingConfig.elasticsearch.enabled = true; + sensorIndexingConfig.solr.index = 'bro'; + sensorIndexingConfig.solr.batchSize = 5; + sensorIndexingConfig.solr.enabled = true; + + let sensorIndexingConfigChangedString = `{"hdfs": {"index": "squid","batchSize": 1,"enabled":true}, + "elasticsearch": {"index": "squid","batchSize": 1,"enabled":true}, + "solr": {"index": "squid","batchSize": 1,"enabled":true}}`; + let sensorIndexingConfigChanged = new IndexingConfigurations(); + sensorIndexingConfigChanged.hdfs.index = 'squid'; + sensorIndexingConfigChanged.hdfs.batchSize = 1; + sensorIndexingConfigChanged.hdfs.enabled = true; + sensorIndexingConfigChanged.elasticsearch.index = 'squid'; + sensorIndexingConfigChanged.elasticsearch.batchSize = 1; + sensorIndexingConfigChanged.elasticsearch.enabled = true; + sensorIndexingConfigChanged.solr.index = 'squid'; + sensorIndexingConfigChanged.solr.batchSize = 1; + sensorIndexingConfigChanged.solr.enabled = true; + + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [SharedModule, SensorRawJsonModule], + }); + + fixture = TestBed.createComponent(SensorRawJsonComponent); + component = fixture.componentInstance; + })); + + it('should create an instance', () => { + expect(component).toBeDefined(); + }); + + it('should create an instance', () => { + spyOn(component, 'init'); + let changes: SimpleChanges = {'showRawJson': new SimpleChange(false, true)}; + + component.ngOnChanges(changes); + expect(component.init).toHaveBeenCalled(); + + changes = {'showRawJson': new SimpleChange(true, false)}; + component.ngOnChanges(changes); + expect(component.init['calls'].count()).toEqual(1); + + fixture.destroy(); + }); + + it('should initialise the fields', () => { + + component.init(); + expect(component.newSensorParserConfig).toEqual(undefined); + expect(component.newSensorEnrichmentConfig).toEqual(undefined); + expect(component.newIndexingConfigurations).toEqual(undefined); + + component.sensorParserConfig = sensorParserConfig; + component.sensorEnrichmentConfig = sensorEnrichmentConfig; + component.indexingConfigurations = sensorIndexingConfig; + component.init(); + expect(component.newSensorParserConfig).toEqual(JSON.stringify(sensorParserConfig, null, '\t')); + expect(component.newSensorEnrichmentConfig).toEqual(JSON.stringify(sensorEnrichmentConfig, null, '\t')); + expect(component.newIndexingConfigurations).toEqual(JSON.stringify(sensorIndexingConfig, null, '\t')); + + fixture.destroy(); + }); + + it('should save the fields', () => { + spyOn(component.hideRawJson, 'emit'); + spyOn(component.onRawJsonChanged, 'emit'); + component.sensorParserConfig = new SensorParserConfig(); + component.sensorEnrichmentConfig = new SensorEnrichmentConfig(); + component.indexingConfigurations = new IndexingConfigurations(); + + component.newSensorParserConfig = sensorParserConfigString; + component.newSensorEnrichmentConfig = sensorEnrichmentConfigString; + component.newIndexingConfigurations = sensorIndexingConfigString; + component.onSave(); + expect(component.sensorParserConfig).toEqual(sensorParserConfig); + expect(component.sensorEnrichmentConfig).toEqual(sensorEnrichmentConfig); + expect(component.indexingConfigurations).toEqual(sensorIndexingConfig); + expect(component.hideRawJson.emit).toHaveBeenCalled(); + expect(component.onRawJsonChanged.emit).toHaveBeenCalled(); + + + component.newSensorParserConfig = sensorParserConfigWithClassNameString; + component.newSensorEnrichmentConfig = sensorEnrichmentConfigWithConfigString; + component.newIndexingConfigurations = sensorIndexingConfigChangedString; + component.onSave(); + expect(component.sensorParserConfig).toEqual(sensorParserConfigWithClassName); + expect(component.sensorEnrichmentConfig).toEqual(sensorEnrichmentConfigWithConfig); + expect(component.indexingConfigurations).toEqual(sensorIndexingConfigChanged); + expect(component.hideRawJson.emit['calls'].count()).toEqual(2); + expect(component.onRawJsonChanged.emit['calls'].count()).toEqual(2); + + fixture.destroy(); + }); + + it('should hide panel', () => { + spyOn(component.hideRawJson, 'emit'); + + component.onCancel(); + + expect(component.hideRawJson.emit).toHaveBeenCalled(); + + fixture.destroy(); + }); +}); http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts new file mode 100644 index 0000000..38d9e90 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.component.ts @@ -0,0 +1,103 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {Component, Input, EventEmitter, Output, OnChanges, SimpleChanges} from '@angular/core'; +import {SensorParserConfig} from '../../model/sensor-parser-config'; +import {SensorEnrichmentConfig, EnrichmentConfig, ThreatIntelConfig} from '../../model/sensor-enrichment-config'; +import {IndexingConfigurations, SensorIndexingConfig} from '../../model/sensor-indexing-config'; + +declare var ace: any; + +@Component({ + selector: 'metron-config-sensor-raw-json', + templateUrl: './sensor-raw-json.component.html', + styleUrls: ['./sensor-raw-json.component.scss'] +}) + +export class SensorRawJsonComponent implements OnChanges { + + @Input() showRawJson: boolean; + @Input() sensorParserConfig: SensorParserConfig; + @Input() sensorEnrichmentConfig: SensorEnrichmentConfig; + @Input() indexingConfigurations: IndexingConfigurations; + + @Output() hideRawJson: EventEmitter<boolean> = new EventEmitter<boolean>(); + @Output() onRawJsonChanged: EventEmitter<boolean> = new EventEmitter<boolean>(); + + newSensorParserConfig: string; + newSensorEnrichmentConfig: string; + newIndexingConfigurations: string; + + ngOnChanges(changes: SimpleChanges) { + if (changes['showRawJson'] && changes['showRawJson'].currentValue) { + this.init(); + } + } + + init(): void { + if (this.sensorParserConfig) { + this.newSensorParserConfig = JSON.stringify(this.sensorParserConfig, null, '\t'); + } + + if (this.sensorEnrichmentConfig) { + this.newSensorEnrichmentConfig = JSON.stringify(this.sensorEnrichmentConfig, null, '\t'); + } + + if (this.indexingConfigurations) { + this.newIndexingConfigurations = JSON.stringify(this.indexingConfigurations, null, '\t'); + } + } + + onSave() { + let newParsedSensorParserConfig = JSON.parse(this.newSensorParserConfig); + this.sensorParserConfig.sensorTopic = newParsedSensorParserConfig.sensorTopic; + this.sensorParserConfig.parserConfig = newParsedSensorParserConfig.parserConfig; + this.sensorParserConfig.parserClassName = newParsedSensorParserConfig.parserClassName; + this.sensorParserConfig.fieldTransformations = newParsedSensorParserConfig.fieldTransformations; + + if (newParsedSensorParserConfig.writerClassName != null) { + this.sensorParserConfig.writerClassName = newParsedSensorParserConfig.writerClassName; + } + if (newParsedSensorParserConfig.errorWriterClassName != null) { + this.sensorParserConfig.errorWriterClassName = newParsedSensorParserConfig.errorWriterClassName; + } + if (newParsedSensorParserConfig.filterClassName != null) { + this.sensorParserConfig.filterClassName = newParsedSensorParserConfig.filterClassName; + } + if (newParsedSensorParserConfig.invalidWriterClassName != null) { + this.sensorParserConfig.invalidWriterClassName = newParsedSensorParserConfig.invalidWriterClassName; + } + + let newParsedSensorEnrichmentConfig = JSON.parse(this.newSensorEnrichmentConfig); + this.sensorEnrichmentConfig.enrichment = Object.assign(new EnrichmentConfig(), newParsedSensorEnrichmentConfig.enrichment); + this.sensorEnrichmentConfig.threatIntel = Object.assign(new ThreatIntelConfig(), newParsedSensorEnrichmentConfig.threatIntel); + if (newParsedSensorEnrichmentConfig.configuration != null) { + this.sensorEnrichmentConfig.configuration = newParsedSensorEnrichmentConfig.configuration; + } + + let newParsedIndexingConfigurations = JSON.parse(this.newIndexingConfigurations); + this.indexingConfigurations.hdfs = Object.assign(new SensorIndexingConfig(), newParsedIndexingConfigurations.hdfs); + this.indexingConfigurations.elasticsearch = Object.assign(new SensorIndexingConfig(), newParsedIndexingConfigurations.elasticsearch); + this.indexingConfigurations.solr = Object.assign(new SensorIndexingConfig(), newParsedIndexingConfigurations.solr); + this.hideRawJson.emit(true); + this.onRawJsonChanged.emit(true); + } + + onCancel(): void { + this.hideRawJson.emit(true); + } +} http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.module.ts b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.module.ts new file mode 100644 index 0000000..965eb73 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-raw-json/sensor-raw-json.module.ts @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { NgModule } from '@angular/core'; +import {SharedModule} from '../../shared/shared.module'; +import {SensorRawJsonComponent} from './sensor-raw-json.component'; +import {AceEditorModule} from '../../shared/ace-editor/ace-editor.module'; + +@NgModule ({ + imports: [ SharedModule, AceEditorModule ], + declarations: [ SensorRawJsonComponent ], + exports: [ SensorRawJsonComponent ] +}) +export class SensorRawJsonModule {} http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html new file mode 100644 index 0000000..1a1f9f0 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.html @@ -0,0 +1,49 @@ +<!-- + Licensed to the Apache Software + Foundation (ASF) under one or more contributor license agreements. See the + NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to You under the Apache License, + Version 2.0 (the "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + OR CONDITIONS OF ANY KIND, either express or implied. See the License for + the specific language governing permissions and limitations under the License. + --> +<div class="metron-slider-pane-edit fill load-left-to-right dialog1x"> + + <div class="form-title">Edit Rule</div> + <i class="fa fa-times pull-right close-button" aria-hidden="true" (click)="onCancel()"></i> + + + <form role="form" class="threat-intel-form"> + <div class="form-group"> + <label attr.for="statement">NAME</label> + <input type="text" class="form-control" name="ruleName" [(ngModel)]="newRiskLevelRule.name"> + </div> + <div class="form-group"> + <label attr.for="statement">TEXT</label> + <textarea rows="30" class="form-control" name="ruleValue" [(ngModel)]="newRiskLevelRule.rule" style="height: inherit"></textarea> + </div> + </form> + <form class="form-inline"> + <div class="form-group"> + <label attr.for="statement" style="width: 100%">SCORE ADJUSTMENT</label> + <input class="score-slider" name="scoreSlider" type="range" min="0" max="100" [(ngModel)]="newRiskLevelRule.score" style="width: 72%; margin-right: 4px"> + <div style="width: 25%; display: inline-block"> + <metron-config-number-spinner name="scoreText" [(ngModel)]="newRiskLevelRule.score" [min]="0" [max]="100"> </metron-config-number-spinner> + </div> + </div> + </form> + + <div class="form-group"> + <div class="form-seperator-edit"></div> + <div class="button-row"> + <button type="submit" class="btn form-enable-disable-button" (click)="onSave()">SAVE</button> + <button class="btn form-enable-disable-button" (click)="onCancel()" >CANCEL</button> + </div> + </div> +</div> http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss new file mode 100644 index 0000000..ca6c7d7 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.scss @@ -0,0 +1,90 @@ +/** + * 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. + */ +$range-gradient-start: #AC9B5A; +$range-gradient-end: #D8747C; +.form-title +{ + padding-left: 25px; +} + +.form-group +{ + padding-left: 25px; + padding-right: 20px; + padding-bottom: 10px; +} + +.close-button +{ + padding-right: 20px; +} + +.score-adjustment +{ + display: inline-block; +} + +.button-row { + padding-top: 10px; +} + +.form-enable-disable-button { + width: 32%; +} + +input[type="range"] { + height: 4px; + -webkit-appearance: none; + -moz-apperance: none; + background: $range-gradient-start; + background: -webkit-linear-gradient(left, $range-gradient-start, $range-gradient-end); + background: -moz-linear-gradient(left, $range-gradient-start, $range-gradient-end); + background: -ms-linear-gradient(left, $range-gradient-start, $range-gradient-end); + background: -o-linear-gradient(left, $range-gradient-start, $range-gradient-end); + background: linear-gradient(to right, $range-gradient-start, $range-gradient-end); + border-radius: 4px; + + &:focus + { + outline: none; + } +} + +input[type="range"]::-webkit-slider-thumb{ + -webkit-appearance:none; + -moz-apperance:none; + width:15px; + height:15px; + -webkit-border-radius:20px; + -moz-border-radius:20px; + -ms-border-radius:20px; + -o-border-radius:20px; + border-radius:20px; + background-color: $range-gradient-start; +} + +input[type="range"]::-moz-range-track +{ + background:transparent; border:0px; +} + +input[type=range]::-moz-range-thumb +{ + background-color: $range-gradient-start; +} + http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts new file mode 100644 index 0000000..2f8cd24 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.spec.ts @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {async, TestBed, ComponentFixture} from '@angular/core/testing'; +import {SensorRuleEditorComponent} from './sensor-rule-editor.component'; +import {SharedModule} from '../../../shared/shared.module'; +import {NumberSpinnerComponent} from '../../../shared/number-spinner/number-spinner.component'; +import {RiskLevelRule} from '../../../model/risk-level-rule'; + +describe('Component: SensorRuleEditorComponent', () => { + + let fixture: ComponentFixture<SensorRuleEditorComponent>; + let component: SensorRuleEditorComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [SharedModule + ], + declarations: [ SensorRuleEditorComponent, NumberSpinnerComponent ], + providers: [ + SensorRuleEditorComponent + ] + }); + + fixture = TestBed.createComponent(SensorRuleEditorComponent); + component = fixture.componentInstance; + })); + + it('should create an instance', () => { + expect(component).toBeDefined(); + }); + + it('should edit rules', async(() => { + let numCancelled = 0; + let savedRule = new RiskLevelRule(); + component.onCancelTextEditor.subscribe((cancelled: boolean) => { + numCancelled++; + }); + component.onSubmitTextEditor.subscribe((rule: RiskLevelRule) => { + savedRule = rule; + }); + + component.riskLevelRule = {name: 'rule1', rule: 'initial rule', score: 1, comment: ''}; + component.ngOnInit(); + component.onSave(); + let rule1 = Object.assign(new RiskLevelRule(), {name: 'rule1', rule: 'initial rule', score: 1, comment: ''}); + expect(savedRule).toEqual(rule1); + + component.riskLevelRule = {name: 'rule2', rule: 'new rule', score: 2, comment: ''}; + component.ngOnInit(); + component.onSave(); + let rule2 = Object.assign(new RiskLevelRule(), {name: 'rule2', rule: 'new rule', score: 2, comment: ''}); + expect(savedRule).toEqual(rule2); + + expect(numCancelled).toEqual(0); + component.onCancel(); + expect(numCancelled).toEqual(1); + })); +}); http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts new file mode 100644 index 0000000..1bdfea1 --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.component.ts @@ -0,0 +1,49 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {Component, Input, EventEmitter, Output, OnInit} from '@angular/core'; +import {RiskLevelRule} from '../../../model/risk-level-rule'; + +@Component({ + selector: 'metron-config-sensor-rule-editor', + templateUrl: './sensor-rule-editor.component.html', + styleUrls: ['./sensor-rule-editor.component.scss'] +}) + +export class SensorRuleEditorComponent implements OnInit { + + @Input() riskLevelRule: RiskLevelRule; + + @Output() onCancelTextEditor: EventEmitter<boolean> = new EventEmitter<boolean>(); + @Output() onSubmitTextEditor: EventEmitter<RiskLevelRule> = new EventEmitter<RiskLevelRule>(); + newRiskLevelRule = new RiskLevelRule(); + + constructor() { } + + ngOnInit() { + Object.assign(this.newRiskLevelRule, this.riskLevelRule); + } + + onSave(): void { + this.onSubmitTextEditor.emit(this.newRiskLevelRule); + } + + onCancel(): void { + this.onCancelTextEditor.emit(true); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-metron/blob/1ef8cd8f/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts ---------------------------------------------------------------------- diff --git a/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts new file mode 100644 index 0000000..a99c8cf --- /dev/null +++ b/metron-interface/metron-config/src/app/sensors/sensor-threat-triage/rule-editor/sensor-rule-editor.module.ts @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { NgModule } from '@angular/core'; +import {SharedModule} from '../../../shared/shared.module'; +import {SensorRuleEditorComponent} from './sensor-rule-editor.component'; +import {NumberSpinnerModule} from '../../../shared/number-spinner/number-spinner.module'; + +@NgModule ({ + imports: [ SharedModule, NumberSpinnerModule ], + declarations: [ SensorRuleEditorComponent ], + exports: [ SensorRuleEditorComponent ] +}) +export class SensorRuleEditorModule {}