diff --git a/src/controllers/analytic_controller.ts b/src/controllers/analytic_controller.ts index 6e11dac..3aed7ab 100644 --- a/src/controllers/analytic_controller.ts +++ b/src/controllers/analytic_controller.ts @@ -45,12 +45,12 @@ export class AnalyticController { private _serverInfo: ServerInfo; constructor(private _panelObject: any, private _analyticService: AnalyticService, private _emitter: Emitter) { - if(_panelObject.anomalyTypes === undefined) { - _panelObject.anomalyTypes = []; + if(_panelObject.analyticUnits === undefined) { + _panelObject.analyticUnits = _panelObject.anomalyTypes || []; } this._labelingDataAddedSegments = new SegmentArray(); this._labelingDataDeletedSegments = new SegmentArray(); - this._analyticUnitsSet = new AnalyticUnitsSet(this._panelObject.anomalyTypes); + this._analyticUnitsSet = new AnalyticUnitsSet(this._panelObject.analyticUnits); // this.analyticUnits.forEach(a => this.runEnabledWaiter(a)); } @@ -101,15 +101,15 @@ export class AnalyticController { get graphLocked() { return this._graphLocked; } set graphLocked(value) { this._graphLocked = value; } - get labelingAnomaly(): AnalyticUnit { + get labelingUnit(): AnalyticUnit { if(this._selectedAnalyticUnitId === null) { return null; } return this._analyticUnitsSet.byId(this._selectedAnalyticUnitId); } - async toggleAnomalyTypeLabelingMode(id: AnalyticUnitId) { - if(this.labelingAnomaly && this.labelingAnomaly.saving) { + async toggleUnitTypeLabelingMode(id: AnalyticUnitId) { + if(this.labelingUnit && this.labelingUnit.saving) { throw new Error('Can`t toggle during saving'); } if(this._selectedAnalyticUnitId === id) { @@ -117,7 +117,7 @@ export class AnalyticController { } await this.disableLabeling(); this._selectedAnalyticUnitId = id; - this.labelingAnomaly.selected = true; + this.labelingUnit.selected = true; this.toggleVisibility(id, true); } @@ -125,22 +125,22 @@ export class AnalyticController { if(this._selectedAnalyticUnitId === null) { return; } - this.labelingAnomaly.saving = true; + this.labelingUnit.saving = true; var newIds = await this._saveLabelingData(); this._labelingDataAddedSegments.getSegments().forEach((s, i) => { - this.labelingAnomaly.segments.updateId(s.id, newIds[i]); + this.labelingUnit.segments.updateId(s.id, newIds[i]); }); - this.labelingAnomaly.saving = false; + this.labelingUnit.saving = false; - var anomaly = this.labelingAnomaly; + var anomaly = this.labelingUnit; this.dropLabeling(); this._runStatusWaiter(anomaly); } undoLabeling() { this._labelingDataAddedSegments.getSegments().forEach(s => { - this.labelingAnomaly.segments.remove(s.id); + this.labelingUnit.segments.remove(s.id); }); this._labelingDataDeletedSegments.getSegments().forEach(s => { s.deleted = false; @@ -151,7 +151,7 @@ export class AnalyticController { dropLabeling() { this._labelingDataAddedSegments.clear(); this._labelingDataDeletedSegments.clear(); - this.labelingAnomaly.selected = false; + this.labelingUnit.selected = false; this._selectedAnalyticUnitId = null; this._tempIdCounted = -1; } @@ -164,11 +164,11 @@ export class AnalyticController { if(!this.labelingMode) { return false; } - return this.labelingAnomaly.deleteMode; + return this.labelingUnit.deleteMode; } addLabelSegment(segment: Segment) { - var asegment = this.labelingAnomaly.addLabeledSegment(segment); + var asegment = this.labelingUnit.addLabeledSegment(segment); this._labelingDataAddedSegments.addSegment(asegment); } @@ -183,11 +183,11 @@ export class AnalyticController { this._analyticUnitsSet.byId(id).color = value; } - fetchAnomalyTypesStatuses() { + fetchAnalyticUnitsStatuses() { this.analyticUnits.forEach(a => this._runStatusWaiter(a)); } - async fetchAnomalyTypesSegments(from: number, to: number) { + async fetchAnalyticUnitsSegments(from: number, to: number) { if(!_.isNumber(+from)) { throw new Error('from isn`t number'); } @@ -215,9 +215,9 @@ export class AnalyticController { } private async _saveLabelingData(): Promise { - var anomaly = this.labelingAnomaly; - if(anomaly === null) { - throw new Error('anomaly is not selected'); + var unit = this.labelingUnit; + if(unit === null) { + throw new Error('analytic unit is not selected'); } if( @@ -228,7 +228,7 @@ export class AnalyticController { } return this._analyticService.updateSegments( - anomaly.id, this._labelingDataAddedSegments, this._labelingDataDeletedSegments + unit.id, this._labelingDataAddedSegments, this._labelingDataDeletedSegments ); } @@ -304,7 +304,7 @@ export class AnalyticController { } deleteLabelingAnomalySegmentsInRange(from: number, to: number) { - var allRemovedSegs = this.labelingAnomaly.removeSegmentsInRange(from, to); + var allRemovedSegs = this.labelingUnit.removeSegmentsInRange(from, to); allRemovedSegs.forEach(s => { if(!this._labelingDataAddedSegments.has(s.id)) { this._labelingDataDeletedSegments.addSegment(s); @@ -317,14 +317,17 @@ export class AnalyticController { if(!this.labelingMode) { throw new Error('Cant enter delete mode is labeling mode disabled'); } - this.labelingAnomaly.deleteMode = !this.labelingAnomaly.deleteMode; + this.labelingUnit.deleteMode = !this.labelingUnit.deleteMode; } - removeAnalyticUnit(id: AnalyticUnitId) { + async removeAnalyticUnit(id: AnalyticUnitId, silent: boolean = false) { if(id === this._selectedAnalyticUnitId) { this.dropLabeling(); } this._analyticUnitsSet.removeItem(id); + if(!silent) { + await this._analyticService.removeAnalyticUnit(id); + } } private async _runStatusWaiter(analyticUnit: AnalyticUnit) { @@ -354,7 +357,7 @@ export class AnalyticController { if(error !== undefined) { analyticUnit.error = error; } - this._emitter.emit('anomaly-type-status-change', analyticUnit); + this._emitter.emit('analytic-unit-status-change', analyticUnit); } if(!analyticUnit.isActiveStatus) { break; diff --git a/src/graph_renderer.ts b/src/graph_renderer.ts index 870b804..fde84e8 100644 --- a/src/graph_renderer.ts +++ b/src/graph_renderer.ts @@ -352,7 +352,7 @@ export class GraphRenderer { ANOMALY_REGION_DELETE_COLOR_LIGHT : ANOMALY_REGION_DELETE_COLOR_DARK; } else { - color = this._analyticController.labelingAnomaly.color; + color = this._analyticController.labelingUnit.color; } fillAlpha = ANOMALY_REGION_FILL_ALPHA; strokeAlpha = ANOMALY_REGION_STROKE_ALPHA; diff --git a/src/models/analytic_unit.ts b/src/models/analytic_unit.ts index fd0fc90..7c72eea 100644 --- a/src/models/analytic_unit.ts +++ b/src/models/analytic_unit.ts @@ -100,6 +100,7 @@ export class AnalyticUnit { get status() { return this._status; } set status(value) { if( + value !== '404' && value !== 'READY' && value !== 'LEARNING' && value !== 'PENDING' && @@ -114,7 +115,13 @@ export class AnalyticUnit { set error(value) { this._error = value; } get isActiveStatus() { - return this.status !== 'READY' && this.status !== 'FAILED'; + switch(this.status) { + case '404': + case 'READY': + case 'FAILED': + return false; + } + return true; } get panelObject() { return this._panelObject; } diff --git a/src/module.ts b/src/module.ts index 3430298..372d4d7 100644 --- a/src/module.ts +++ b/src/module.ts @@ -17,6 +17,9 @@ import { axesEditorComponent } from './axes_editor'; import { MetricsPanelCtrl, alertTab } from 'grafana/app/plugins/sdk'; import { appEvents } from 'grafana/app/core/core' +import { BackendSrv } from 'grafana/app/core/services/backend_srv'; +import { AlertSrv } from 'grafana/app/core/services/alert_srv'; + import config from 'grafana/app/core/config'; import _ from 'lodash'; @@ -148,10 +151,13 @@ class GraphCtrl extends MetricsPanelCtrl { /** @ngInject */ constructor( - $scope, $injector, $http, private annotationsSrv, - private keybindingSrv, private backendSrv, - private popoverSrv, private contextSrv, - private alertSrv + $scope, $injector, $http, + private annotationsSrv, + private keybindingSrv, + private backendSrv: BackendSrv, + private popoverSrv, + private contextSrv, + private alertSrv: AlertSrv ) { super($scope, $injector); @@ -162,8 +168,7 @@ class GraphCtrl extends MetricsPanelCtrl { this.processor = new DataProcessor(this.panel); - - this.analyticService = new AnalyticService(this.backendURL, $http, this.alertSrv); + this.analyticService = new AnalyticService(this.backendURL, $http, this.backendSrv, this.alertSrv); this.runBackendConnectivityCheck(); @@ -180,12 +185,15 @@ class GraphCtrl extends MetricsPanelCtrl { this.events.on('anomaly-type-alert-change', () => { this.$scope.$digest() }); - this.events.on('anomaly-type-status-change', async (anomalyType: AnalyticUnit) => { - if(anomalyType === undefined) { - throw new Error('anomalyType is undefined'); + this.events.on('analytic-unit-status-change', async (analyticUnit: AnalyticUnit) => { + if(analyticUnit === undefined) { + throw new Error('analyticUnit is undefined'); + } + if(analyticUnit.status === '404') { + await this.analyticsController.removeAnalyticUnit(analyticUnit.id, true); } - if(anomalyType.status === 'READY') { - await this.analyticsController.fetchSegments(anomalyType, +this.range.from, +this.range.to); + if(analyticUnit.status === 'READY') { + await this.analyticsController.fetchSegments(analyticUnit, +this.range.from, +this.range.to); } this.render(this.seriesList); this.$scope.$digest(); @@ -203,7 +211,7 @@ class GraphCtrl extends MetricsPanelCtrl { }; }); - this.analyticsController.fetchAnomalyTypesStatuses(); + this.analyticsController.fetchAnalyticUnitsStatuses(); } @@ -326,7 +334,7 @@ class GraphCtrl extends MetricsPanelCtrl { var loadTasks = [ // this.annotationsPromise, - this.analyticsController.fetchAnomalyTypesSegments(+this.range.from, +this.range.to) + this.analyticsController.fetchAnalyticUnitsSegments(+this.range.from, +this.range.to) ]; var results = await Promise.all(loadTasks); @@ -558,11 +566,11 @@ class GraphCtrl extends MetricsPanelCtrl { this.render(); } - onRemove(id: AnalyticUnitId) { + async onRemove(id: AnalyticUnitId) { if(id === undefined) { throw new Error('id is undefined'); } - this.analyticsController.removeAnalyticUnit(id); + await this.analyticsController.removeAnalyticUnit(id); this.render(); } @@ -584,7 +592,7 @@ class GraphCtrl extends MetricsPanelCtrl { } async onToggleLabelingMode(key) { - await this.analyticsController.toggleAnomalyTypeLabelingMode(key as AnalyticUnitId); + await this.analyticsController.toggleUnitTypeLabelingMode(key as AnalyticUnitId); this.$scope.$digest(); this.render(); } diff --git a/src/services/analytic_service.ts b/src/services/analytic_service.ts index 030b18a..cb25679 100644 --- a/src/services/analytic_service.ts +++ b/src/services/analytic_service.ts @@ -4,36 +4,45 @@ import { DatasourceRequest } from '../models/datasource'; import { SegmentsSet } from '../models/segment_set'; import { AnalyticUnitId, AnalyticUnit, AnalyticSegment } from '../models/analytic_unit'; import { ServerInfo } from '../models/info'; +import { BackendSrv } from 'grafana/app/core/services/backend_srv'; +import { AlertSrv } from 'grafana/app/core/services/alert_srv'; export class AnalyticService { private _isUp = false; - constructor(private _backendURL: string, private $http, private alertSrv) { + constructor( + private _backendURL: string, private $http, + private _backendSrv: BackendSrv, + private _alertSrv: AlertSrv + ) { this.isBackendOk(); } async postNewItem( - metric: MetricExpanded, datasourceRequest: DatasourceRequest, + metric: MetricExpanded, datasourceRequest: DatasourceRequest, newItem: AnalyticUnit, panelId: number ): Promise { - let datasource = await this.get(`/api/datasources/name/${metric.datasource}`); + let datasource = await this._backendSrv.get(`/api/datasources/name/${metric.datasource}`); datasourceRequest.type = datasource.type; - const response = await this.post(this._backendURL + '/analyticUnits', { + const response = await this.post('/analyticUnits', { panelUrl: window.location.origin + window.location.pathname + `?panelId=${panelId}&fullscreen`, type: newItem.type, name: newItem.name, metric: metric.toJSON(), datasource: datasourceRequest }); - + return response.id as AnalyticUnitId; } - async isBackendOk(): Promise { - await this.get(this._backendURL); + async removeAnalyticUnit(id: AnalyticUnitId) { + return this.delete('/analyticUnits', { id }); + } + async isBackendOk(): Promise { + await this.get('/'); return this._isUp; } @@ -51,7 +60,7 @@ export class AnalyticService { removedSegments: removedSegments.getSegments().map(s => s.id) }; - var data = await this.patch(this._backendURL + '/segments', payload); + var data = await this.patch('/segments', payload); if(data.addedIds === undefined) { throw new Error('Server didn`t send addedIds'); } @@ -69,7 +78,7 @@ export class AnalyticService { if(to !== undefined) { payload['to'] = to; } - var data = await this.get(this._backendURL + '/segments', payload); + var data = await this.get('/segments', payload); if(data.segments === undefined) { throw new Error('Server didn`t return segments array'); } @@ -77,13 +86,21 @@ export class AnalyticService { return segments.map(s => new AnalyticSegment(s.labeled, s.id, s.from, s.to, s.deleted)); } - async * getStatusGenerator(id: AnalyticUnitId, duration: number) { + async * getStatusGenerator(id: AnalyticUnitId, duration: number): + AsyncIterableIterator<{ status: string, errorMessage?: string }> { + if(id === undefined) { throw new Error('id is undefined'); } let statusCheck = async () => { - var data = await this.get(this._backendURL + '/analyticUnits/status', { id }); - return data; + try { + return await this.get('/analyticUnits/status', { id }); + } catch(error) { + if(error.status === 404) { + return { status: '404' }; + } + throw error; + } } let timeout = async () => new Promise( @@ -100,7 +117,7 @@ export class AnalyticService { if(id === undefined) { throw new Error('id is undefined'); } - var data = await this.get(this._backendURL + '/alerts', { id }); + var data = await this.get('/alerts', { id }); if(data.enabled === undefined) { throw new Error('Server didn`t return "enabled"'); } @@ -111,12 +128,11 @@ export class AnalyticService { if(id === undefined) { throw new Error('id is undefined'); } - return await this.post(this._backendURL + '/alerts', { id, enabled }); + return await this.post('/alerts', { id, enabled }); } async getServerInfo(): Promise { - let data = await this.get(this._backendURL); - console.log(data); + let data = await this.get('/'); return { nodeVersion: data.nodeVersion, packageVersion: data.packageVersion, @@ -129,44 +145,48 @@ export class AnalyticService { }; } - private async get(url, params?) { + private async _analyticRequest(method: string, url: string, data?) { try { - let response = await this.$http({ method: 'GET', url, params }); + method = method.toUpperCase(); + url = this._backendURL + url; + let requestObject: any = { method, url }; + if(method === 'GET' || method === 'DELETE') { + requestObject.params = data; + } else { + requestObject.data = data; + } + let response = await this.$http(requestObject); this._isUp = true; return response.data; } catch(error) { - this.displayConnectionAlert(); - console.error(error); - this._isUp = false; - } + if(error.xhrStatus === 'error') { + this.displayConnectionAlert(); + this._isUp = false; + } else { + this._isUp = true; + } + throw error; + } } - private async post(url, data) { - try { - let response = await this.$http({ method: 'POST', url, data }); - this._isUp = true; - return response.data; - } catch(error) { - this.displayConnectionAlert(); - console.error(error); - this._isUp = false; - } + private async get(url, params?) { + return this._analyticRequest('GET', url, params); } - private async patch(url, data) { - try { - let response = await this.$http({ method: 'PATCH', url, data }); - this._isUp = true; - return response.data; - } catch(error) { - this.displayConnectionAlert(); - console.error(error); - this._isUp = false; - } + private async post(url, data?) { + return this._analyticRequest('POST', url, data); + } + + private async patch(url, data?) { + return this._analyticRequest('PATCH', url, data); + } + + private async delete(url, data?) { + return this._analyticRequest('DELETE', url, data); } private displayConnectionAlert() { - this.alertSrv.set( + this._alertSrv.set( 'No connection to Hastic server', `Hastic server: "${this._backendURL}"`, 'warning', 4000