This is an automated email from the ASF dual-hosted git repository. sardell pushed a commit to branch feature/METRON-1856-parser-aggregation in repository https://gitbox.apache.org/repos/asf/metron.git
The following commit(s) were added to refs/heads/feature/METRON-1856-parser-aggregation by this push: new 130f97f METRON-2115 [UI] Aligning UI to the parser aggregation API (tiborm via sardell) closes apache/metron#1414 130f97f is described below commit 130f97f39e7b9e223e8a431ceec1b45096bd38fa Author: tiborm <tibor.mel...@gmail.com> AuthorDate: Mon Jul 15 13:40:42 2019 +0200 METRON-2115 [UI] Aligning UI to the parser aggregation API (tiborm via sardell) closes apache/metron#1414 --- ...sensor-parser-config-readonly.component.spec.ts | 4 +- .../sensor-parser-config-readonly.component.ts | 2 +- .../sensor-parser-config-history.service.spec.ts | 8 +- .../service/sensor-parser-config.service.spec.ts | 374 ++++++++++++++++++++- .../app/service/sensor-parser-config.service.ts | 172 ++++++++-- 5 files changed, 513 insertions(+), 47 deletions(-) diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts index 16628d1..4ebfb6d 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.spec.ts @@ -645,7 +645,7 @@ describe('Component: SensorParserConfigReadonly', () => { it('onDeleteSensor should delete the sensor', async(() => { spyOn( sensorParserConfigService, - 'deleteSensorParserConfig' + 'deleteConfig' ).and.returnValue( Observable.create(observer => { observer.next({}); @@ -662,7 +662,7 @@ describe('Component: SensorParserConfigReadonly', () => { component.onDeleteSensor(); expect( - sensorParserConfigService.deleteSensorParserConfig + sensorParserConfigService.deleteConfig ).toHaveBeenCalledWith('abc'); expect(alerts.showSuccessMessage).toHaveBeenCalledWith( 'Deleted sensor abc' diff --git a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts index bbc04f4..04c8c69 100644 --- a/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts +++ b/metron-interface/metron-config/src/app/sensors/sensor-parser-config-readonly/sensor-parser-config-readonly.component.ts @@ -444,7 +444,7 @@ export class SensorParserConfigReadonlyComponent implements OnInit { this.toggleStartStopInProgress(); let name = this.selectedSensorName; - this.sensorParserConfigService.deleteSensorParserConfig(name).subscribe( + this.sensorParserConfigService.deleteConfig(name).subscribe( result => { this.metronAlerts.showSuccessMessage('Deleted sensor ' + name); this.toggleStartStopInProgress(); diff --git a/metron-interface/metron-config/src/app/service/sensor-parser-config-history.service.spec.ts b/metron-interface/metron-config/src/app/service/sensor-parser-config-history.service.spec.ts index 4816ae5..ae7de24 100644 --- a/metron-interface/metron-config/src/app/service/sensor-parser-config-history.service.spec.ts +++ b/metron-interface/metron-config/src/app/service/sensor-parser-config-history.service.spec.ts @@ -16,15 +16,15 @@ * limitations under the License. */ import { TestBed } from '@angular/core/testing'; -import { SensorParserConfig } from '../model/sensor-parser-config'; +import { ParserConfigModel } from '../sensors/models/parser-config.model'; import { SensorParserConfigHistoryService } from './sensor-parser-config-history.service'; import { SensorParserConfigHistory } from '../model/sensor-parser-config-history'; import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; -import {AppConfigService} from './app-config.service'; -import {MockAppConfigService} from './mock.app-config.service'; +import { AppConfigService } from './app-config.service'; +import { MockAppConfigService } from './mock.app-config.service'; describe('SensorParserConfigHistoryService', () => { let mockBackend: HttpTestingController; @@ -50,7 +50,7 @@ describe('SensorParserConfigHistoryService', () => { describe('when service functions', () => { let sensorParserConfigHistory = new SensorParserConfigHistory(); - sensorParserConfigHistory.config = new SensorParserConfig(); + sensorParserConfigHistory.config = new ParserConfigModel('TestConfigId01'); it('get', () => { sensorParserConfigHistoryService.get('bro').subscribe( diff --git a/metron-interface/metron-config/src/app/service/sensor-parser-config.service.spec.ts b/metron-interface/metron-config/src/app/service/sensor-parser-config.service.spec.ts index 6e63113..b138017 100644 --- a/metron-interface/metron-config/src/app/service/sensor-parser-config.service.spec.ts +++ b/metron-interface/metron-config/src/app/service/sensor-parser-config.service.spec.ts @@ -67,7 +67,7 @@ describe('SensorParserConfigService', () => { it('post', () => { sensorParserConfigService - .post('bro', sensorParserConfig) + .saveConfig('bro', sensorParserConfig) .subscribe(result => { expect(result).toEqual(sensorParserConfig); }); @@ -78,7 +78,7 @@ describe('SensorParserConfigService', () => { }); it('get', () => { - sensorParserConfigService.get('bro').subscribe(result => { + sensorParserConfigService.getConfig('bro').subscribe(result => { expect(result).toEqual(sensorParserConfig); }); const req = mockBackend.expectOne('/api/v1/sensor/parser/config/bro'); @@ -87,7 +87,7 @@ describe('SensorParserConfigService', () => { }); it('getAll', () => { - sensorParserConfigService.getAll().subscribe(results => { + sensorParserConfigService.getAllConfig().subscribe(results => { expect(results).toEqual([sensorParserConfig]); }); const req = mockBackend.expectOne('/api/v1/sensor/parser/config'); @@ -122,7 +122,7 @@ describe('SensorParserConfigService', () => { it('deleteSensorParserConfigs', () => { let req = []; sensorParserConfigService - .deleteSensorParserConfigs(['bro1', 'bro2']) + .deleteConfigs(['bro1', 'bro2']) .subscribe(result => { expect(result.success.length).toEqual(2); }); @@ -133,4 +133,370 @@ describe('SensorParserConfigService', () => { r.flush(parsedMessage); }); }); + + describe('REST Calls for Parser Grouping', () => { + + it('getting list of parser groups', () => { + sensorParserConfigService.getAllGroups().subscribe((result: ParserGroupModel[]) => { + expect(result.length).toBe(2); + expect(result[0].name).toBe('TestGroupName1'); + expect(result[0].description).toBe('TestDesc1'); + }); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group'); + request.flush([ + { + name: 'TestGroupName1', + description: 'TestDesc1' + }, + { + name: 'TestGroupName2', + description: 'TestDesc2' + } + ]); + }); + + it('getting single parser group by name', () => { + sensorParserConfigService.getGroup('TestGroup').subscribe((result: ParserGroupModel) => { + expect(result.name).toBe('TestGroupName1'); + expect(result.description).toBe('TestDesc1'); + }); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup'); + request.flush({ + name: 'TestGroupName1', + description: 'TestDesc1' + }); + }); + + it('creating/editing single parser group by name', () => { + sensorParserConfigService.saveGroup('TestGroup', new ParserGroupModel({ + name: 'TestGroupName1', + description: 'TestDesc1', + sensors: ['foo'] + })).subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group'); + expect(request.request.method).toEqual('POST'); + expect(request.request.body.name).toBe('TestGroupName1'); + expect(request.request.body.description).toBe('TestDesc1'); + expect(request.request.body.sensors).toEqual(['foo']); + }); + + it('deleting single parser group by name', () => { + sensorParserConfigService.deleteGroup('TestGroup').subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup'); + expect(request.request.method).toEqual('DELETE'); + }); + + it('deleting multiple parser groups by name', () => { + sensorParserConfigService.deleteGroups(['TestGroup1', 'TestGroup2', 'TestGroup3']) + .subscribe((result) => { + expect(result.success.length).toBe(2); + expect(result.failure.length).toBe(1); + }); + + const request: Array<TestRequest> = []; + request.push(mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup1')); + request.push(mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup2')); + request.push(mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup3')); + + expect(request[0].request.method).toEqual('DELETE'); + expect(request[1].request.method).toEqual('DELETE'); + expect(request[2].request.method).toEqual('DELETE'); + + request[0].flush({}); + request[1].flush('Invalid request parameters', { status: 404, statusText: 'Bad Request' }); + request[2].flush({}); + }); + + function getTestGroups() { + return [ + { config: new ParserGroupModel({ name: 'TestGroup01', description: '' }) }, + { config: new ParserGroupModel({ name: 'TestGroup02', description: '' }) }, + { config: new ParserGroupModel({ name: 'TestGroup03', description: '' }) }, + { config: new ParserGroupModel({ name: 'TestGroup04', description: '' }) }, + ]; + } + + function getTestConfigs() { + return [ + { config: new ParserConfigModel('Parser_Config_ID_01', { sensorTopic: 'Kafka/Sensor Topic ID 01' }) }, + { config: new ParserConfigModel('Parser_Config_ID_02', { sensorTopic: 'Kafka/Sensor Topic ID 02' }) }, + { config: new ParserConfigModel('Parser_Config_ID_03', { sensorTopic: 'Kafka/Sensor Topic ID 03' }) }, + { config: new ParserConfigModel('Parser_Config_ID_04', { sensorTopic: 'Kafka/Sensor Topic ID 04' }) }, + ]; + } + + function markElementOnIndexAs(testData: ParserMetaInfoModel[], indexes: number[], flag: string) { + indexes.forEach((index) => { + testData[index][flag] = true; + }) + } + + class DirtyFlags { + static NEW = 'isPhantom'; + static CHANGED = 'isDirty'; + static DELETED = 'isDeleted'; + } + + it('syncronizing list of parser GROUPS with the backend - SINGLE DELETE', () => { + const testData = getTestGroups(); + + markElementOnIndexAs(testData, [1], DirtyFlags.DELETED); + + sensorParserConfigService.syncGroups(testData).subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup02'); + expect(request.request.method).toEqual('DELETE'); + }); + + it('syncronizing list of parser GROUPS with the backend - MULTIPLE DELETE', () => { + const testData = getTestGroups(); + + markElementOnIndexAs(testData, [0, 2, 3], DirtyFlags.DELETED); + + sensorParserConfigService.syncGroups(testData).subscribe(); + + const requests = []; + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup01')); + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup04')); + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/group/TestGroup03')); + expect(requests[0].request.method).toEqual('DELETE'); + expect(requests[1].request.method).toEqual('DELETE'); + expect(requests[2].request.method).toEqual('DELETE'); + }); + + it('syncronizing list of parser GROUPS with the backend - SINGLE NEW', () => { + const testData = getTestGroups(); + + markElementOnIndexAs(testData, [0], DirtyFlags.NEW); + + sensorParserConfigService.syncGroups(testData).subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group'); + expect(request.request.method).toEqual('POST'); + expect(request.request.body.name).toEqual('TestGroup01'); + expect(request.request.body.description).toEqual(''); + }); + + it('syncronizing list of parser GROUPS with the backend - MULTIPLE NEW', () => { + const testData = getTestGroups(); + + markElementOnIndexAs(testData, [0, 2], DirtyFlags.NEW); + + sensorParserConfigService.syncGroups(testData).subscribe(); + + const calls = mockBackend.match((request) => { + return request.url.match(/\/api\/v1\/sensor\/parser\/group/) + && request.method === 'POST'; + }); + + expect(calls.length).toBe(2); + expect(calls[0].request.body.name).toEqual('TestGroup01'); + expect(calls[1].request.body.name).toEqual('TestGroup03'); + }); + + it('syncronizing list of parser GROUPS with the backend - SINGLE CHANGED', () => { + const testData = getTestGroups(); + + markElementOnIndexAs(testData, [3], DirtyFlags.CHANGED); + + sensorParserConfigService.syncGroups(testData).subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group'); + expect(request.request.method).toEqual('POST'); + expect(request.request.body.name).toEqual('TestGroup04'); + expect(request.request.body.description).toEqual(''); + }); + + it('syncronizing list of parser GROUPS with the backend - MULTIPLE CHANGED', () => { + const testData = getTestGroups(); + + markElementOnIndexAs(testData, [0, 2], DirtyFlags.CHANGED); + + sensorParserConfigService.syncGroups(testData).subscribe(); + + const calls = mockBackend.match(request => { + return request.url.match(/\/api\/v1\/sensor\/parser\/group/) + && request.method === 'POST'; + }); + + expect(calls.length).toBe(2); + expect(calls[0].request.body.name).toEqual('TestGroup01'); + expect(calls[1].request.body.name).toEqual('TestGroup03'); + }); + + it('syncronizing list of PARSER CONFIGS with the backend - SINGLE DELETE', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [1], DirtyFlags.DELETED); + + sensorParserConfigService.syncConfigs(testData).subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_02'); + expect(request.request.method).toEqual('DELETE'); + }); + + it('syncronizing list of PARSER CONFIGS with the backend - MULTIPLE DELETE', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [0, 2, 3], DirtyFlags.DELETED); + + sensorParserConfigService.syncConfigs(testData).subscribe(); + + const requests = []; + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_01')); + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_03')); + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_04')); + expect(requests[0].request.method).toEqual('DELETE'); + expect(requests[1].request.method).toEqual('DELETE'); + expect(requests[2].request.method).toEqual('DELETE'); + }); + + it('syncronizing list of PARSER CONFIGS with the backend - SINGLE NEW', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [0], DirtyFlags.NEW); + + sensorParserConfigService.syncConfigs(testData).subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_01'); + expect(request.request.method).toEqual('POST'); + expect(JSON.parse(request.request.body).sensorTopic).toEqual('Kafka/Sensor Topic ID 01'); + }); + + it('syncronizing list of PARSER CONFIGS with the backend - MULTIPLE NEW', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [0, 2], DirtyFlags.NEW); + + sensorParserConfigService.syncConfigs(testData).subscribe(); + + const requests = []; + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_01')); + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_03')); + expect(requests[0].request.method).toEqual('POST'); + expect(requests[1].request.method).toEqual('POST'); + + expect(JSON.parse(requests[0].request.body).sensorTopic).toEqual('Kafka/Sensor Topic ID 01'); + expect(JSON.parse(requests[1].request.body).sensorTopic).toEqual('Kafka/Sensor Topic ID 03'); + }); + + it('syncronizing list of PARSER CONFIGS with the backend - SINGLE CHANGED', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [3], DirtyFlags.CHANGED); + + sensorParserConfigService.syncConfigs(testData).subscribe(); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_04'); + expect(request.request.method).toEqual('POST'); + expect(JSON.parse(request.request.body).sensorTopic).toEqual('Kafka/Sensor Topic ID 04'); + }); + + it('syncronizing list of PARSER CONFIGS with the backend - MULTIPLE CHANGED', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [0, 2], DirtyFlags.CHANGED); + + sensorParserConfigService.syncConfigs(testData).subscribe(); + + const requests = []; + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_01')); + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_03')); + expect(requests[0].request.method).toEqual('POST'); + expect(requests[1].request.method).toEqual('POST'); + + expect(JSON.parse(requests[0].request.body).sensorTopic).toEqual('Kafka/Sensor Topic ID 01'); + expect(JSON.parse(requests[1].request.body).sensorTopic).toEqual('Kafka/Sensor Topic ID 03'); + }); + + it('syncronization of PARSER CONFIGS should return with an Observable array of successful/unsuccessful requests', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [0, 2], DirtyFlags.CHANGED); + + sensorParserConfigService.syncConfigs(testData) + .subscribe((syncResults: any[]) => { + expect(syncResults.length === 2); + }); + + const requests = []; + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_01')); + requests.push(mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_03')); + requests[0].flush(requests[0].request.body); + requests[1].flush(requests[1].request.body); + }); + + it('error throwing in syncConfigs()', () => { + const testData = getTestConfigs(); + + markElementOnIndexAs(testData, [2], DirtyFlags.CHANGED); + + sensorParserConfigService.syncConfigs(testData) + .subscribe( + noop, + (error) => { + expect(error).toBeDefined(); + } + ); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/config/Parser_Config_ID_03'); + request.flush('Invalid request parameters', { status: 404, statusText: 'Bad Request' }); + }); + + it('error throwing in syncConfigs()', () => { + const testData = getTestGroups(); + + markElementOnIndexAs(testData, [1], DirtyFlags.CHANGED); + + sensorParserConfigService.syncGroups(testData) + .subscribe( + noop, + (error) => { + expect(error).toBeDefined(); + } + ); + + const request = mockBackend.expectOne('/api/v1/sensor/parser/group'); + request.flush('Invalid request parameters', { status: 404, statusText: 'Bad Request' }); + }); + + it('syncConfigs() should complete even if no changed item passed', () => { + const testData = getTestConfigs(); + + sensorParserConfigService.syncConfigs(testData) + .subscribe( + (value) => { + // we expect sync to return an empty array of result if no changed item + expect(value).toEqual([]); + }, + noop, + () => { + // complete has to be called + expect(true).toBeTruthy(); + } + ); + }); + + it('syncGroups() should complete even if no changed item passed', () => { + const testData = getTestGroups(); + + sensorParserConfigService.syncGroups(testData) + .subscribe( + (value) => { + // we expect sync to return an empty array of result if no changed item + expect(value).toEqual([]); + }, + noop, + () => { + // complete has to be called + expect(true).toBeTruthy(); + } + ); + }); + + }) }); diff --git a/metron-interface/metron-config/src/app/service/sensor-parser-config.service.ts b/metron-interface/metron-config/src/app/service/sensor-parser-config.service.ts index add7cb5..7fb83a6 100644 --- a/metron-interface/metron-config/src/app/service/sensor-parser-config.service.ts +++ b/metron-interface/metron-config/src/app/service/sensor-parser-config.service.ts @@ -15,20 +15,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Injectable, Inject } from '@angular/core'; +import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Observable, Subject } from 'rxjs'; -import { catchError, map } from 'rxjs/operators'; -import { SensorParserConfig } from '../model/sensor-parser-config'; +import { Observable, Subject, from, of } from 'rxjs'; +import { catchError, map, take, mergeMap, finalize, filter, reduce } from 'rxjs/operators'; +import { ParserConfigModel } from '../sensors/models/parser-config.model'; import { HttpUtil } from '../util/httpUtil'; import { ParseMessageRequest } from '../model/parse-message-request'; import { RestError } from '../model/rest-error'; -import {AppConfigService} from './app-config.service'; +import { ParserGroupModel } from '../sensors/models/parser-group.model'; +import { ParserModel } from 'app/sensors/models/parser.model'; +import { ParserMetaInfoModel } from '../sensors/models/parser-meta-info.model'; +import { AppConfigService } from './app-config.service'; @Injectable() export class SensorParserConfigService { - url = this.appConfigService.getApiRoot() + '/sensor/parser/config'; - selectedSensorParserConfig: SensorParserConfig; dataChangedSource = new Subject<string[]>(); dataChanged$ = this.dataChangedSource.asObservable(); @@ -38,57 +39,140 @@ export class SensorParserConfigService { private appConfigService: AppConfigService ) {} - public post( - name: string, - sensorParserConfig: SensorParserConfig - ): Observable<SensorParserConfig> { - return this.http - .post(this.url + '/' + name, JSON.stringify(sensorParserConfig)) - .pipe( - map(HttpUtil.extractData), - catchError(HttpUtil.handleError) - ); + private getParserConfigSvcUrl(): string { + return this.appConfigService.getApiRoot() + '/sensor/parser/config'; } - public get(name: string): Observable<SensorParserConfig> { - return this.http.get(this.url + '/' + name).pipe( - map(HttpUtil.extractData), + private getParserGroupSvcUrl(): string { + return this.appConfigService.getApiRoot() + '/sensor/parser/group'; + } + + public getAllGroups(): Observable<ParserGroupModel[] | RestError> { + function extractParserGroups(raw) { + return Object.keys(raw).map((groupName) => { + return new ParserGroupModel({ + ...raw[groupName] + }) + }); + } + return this.http.get(this.getParserGroupSvcUrl()).pipe( + map(extractParserGroups), catchError(HttpUtil.handleError) ); } - public getAll(): Observable<{}> { - return this.http.get(this.url).pipe( + public getGroup(name: string): Observable<RestError | ParserGroupModel> { + return this.http.get(`${this.getParserGroupSvcUrl()}/${name}`).pipe( + map(group => new ParserGroupModel(group)), + catchError(HttpUtil.handleError) + ); + } + + public saveGroup(name: string, group: ParserGroupModel | ParserModel): Observable<RestError | ParserGroupModel> { + return this.http.post(`${this.getParserGroupSvcUrl()}`, group).pipe( map(HttpUtil.extractData), catchError(HttpUtil.handleError) ); } - public deleteSensorParserConfig( - name: string - ): Observable<Object | RestError> { - return this.http - .delete(this.url + '/' + name) - .pipe(catchError(HttpUtil.handleError)); + public deleteGroup(groupName: string): Observable<{ groupName: string, isSuccess: boolean }> { + return this.http.delete(`${this.getParserGroupSvcUrl()}/${groupName}`).pipe( + map((result) => { return { groupName, isSuccess: true } }), + catchError((error) => { return of({ groupName, isSuccess: false }) }) + ); } - public getAvailableParsers(): Observable<{}> { - return this.http.get(this.url + '/list/available').pipe( + public deleteGroups( + groupNames: string[] + ): Observable<{ success: Array<string>; failure: Array<string> }> { + let result: { success: Array<string>; failure: Array<string> } = { + success: [], + failure: [] + }; + let observable = Observable.create(observer => { + let completed = () => { + if (observer) { + observer.next(result); + observer.complete(); + } + this.dataChangedSource.next(groupNames); + }; + from(groupNames).pipe( + mergeMap(this.deleteGroup.bind(this)), + take(groupNames.length), + map((deleteResult: { groupName: string, isSuccess: boolean}) => { + (deleteResult.isSuccess ? result.success : result.failure).push(deleteResult.groupName); + }), + finalize(completed) + ).subscribe(); + }); + + return observable; + } + + syncConfigs(configs: ParserMetaInfoModel[]): Observable<{}> { + return this.sync(configs, this.saveConfig, this.deleteConfig); + } + + syncGroups(groups: ParserMetaInfoModel[]): Observable<{}> { + return this.sync(groups, this.saveGroup, this.deleteGroup); + } + + private sync( + items: ParserMetaInfoModel[], + saveFn: Function, deleteFn: Function + ) { + return from(items).pipe( + filter(item => !!(item.isDeleted || item.isDirty || item.isPhantom)), + mergeMap((changedItem: ParserMetaInfoModel) => { + if (changedItem.isDeleted) { + return deleteFn.call(this, changedItem.config.getName()); + } else { + return saveFn.call(this, changedItem.config.getName(), changedItem.config); + } + }), + catchError(HttpUtil.handleError), + reduce((acc, value) => { + return acc.concat(value); + }, []) + ) + } + + public getConfig(name: string): Observable<ParserConfigModel> { + return this.http.get(this.getParserConfigSvcUrl() + '/' + name).pipe( map(HttpUtil.extractData), catchError(HttpUtil.handleError) ); } - public parseMessage( - parseMessageRequest: ParseMessageRequest - ): Observable<{}> { - return this.http.post(this.url + '/parseMessage', parseMessageRequest).pipe( + public getAllConfig(): Observable<{}> { + return this.http.get(this.getParserConfigSvcUrl()).pipe( map(HttpUtil.extractData), catchError(HttpUtil.handleError) ); } - public deleteSensorParserConfigs( + public saveConfig( + name: string, + sensorParserConfig: ParserModel + ): Observable<ParserConfigModel> { + return this.http + .post(this.getParserConfigSvcUrl() + '/' + name, JSON.stringify(sensorParserConfig)) + .pipe( + map(HttpUtil.extractData), + catchError(HttpUtil.handleError) + ); + } + + public deleteConfig( + name: string + ): Observable<Object | RestError> { + return this.http + .delete(this.getParserConfigSvcUrl() + '/' + name) + .pipe(catchError(HttpUtil.handleError)); + } + + public deleteConfigs( sensorNames: string[] ): Observable<{ success: Array<string>; failure: Array<string> }> { let result: { success: Array<string>; failure: Array<string> } = { @@ -105,7 +189,7 @@ export class SensorParserConfigService { this.dataChangedSource.next(sensorNames); }; for (let i = 0; i < sensorNames.length; i++) { - this.deleteSensorParserConfig(sensorNames[i]).subscribe( + this.deleteConfig(sensorNames[i]).subscribe( results => { result.success.push(sensorNames[i]); if ( @@ -130,4 +214,20 @@ export class SensorParserConfigService { return observable; } + + public getAvailableParsers(): Observable<{}> { + return this.http.get(this.getParserConfigSvcUrl() + '/list/available').pipe( + map(HttpUtil.extractData), + catchError(HttpUtil.handleError) + ); + } + + public parseMessage( + parseMessageRequest: ParseMessageRequest + ): Observable<{}> { + return this.http.post(this.getParserConfigSvcUrl() + '/parseMessage', parseMessageRequest).pipe( + map(HttpUtil.extractData), + catchError(HttpUtil.handleError) + ); + } }