Browse Source

Synchronize data with server #64 && Leave only important part of panelUrl #102 (#216)

master
rozetko 5 years ago committed by GitHub
parent
commit
781d80531e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 53
      src/panel/graph_panel/controllers/analytic_controller.ts
  2. 45
      src/panel/graph_panel/graph_ctrl.ts
  3. 76
      src/panel/graph_panel/models/analytic_unit.ts
  4. 2
      src/panel/graph_panel/partials/tab_analytics.html
  5. 18
      src/panel/graph_panel/services/analytic_service.ts
  6. 10
      tests/analytic_controller.jest.ts
  7. 2
      tests/setup_tests.ts

53
src/panel/graph_panel/controllers/analytic_controller.ts

@ -49,16 +49,16 @@ export class AnalyticController {
private _thresholds: Threshold[]; private _thresholds: Threshold[];
constructor( constructor(
private _grafanaUrl: string,
private _panelId: string,
private _panelObject: any, private _panelObject: any,
private _emitter: Emitter, private _emitter: Emitter,
private _analyticService?: AnalyticService, private _analyticService?: AnalyticService,
) { ) {
if(_panelObject.analyticUnits === undefined) {
_panelObject.analyticUnits = _panelObject.anomalyTypes || [];
}
this._labelingDataAddedSegments = new SegmentArray<AnalyticSegment>(); this._labelingDataAddedSegments = new SegmentArray<AnalyticSegment>();
this._labelingDataRemovedSegments = new SegmentArray<AnalyticSegment>(); this._labelingDataRemovedSegments = new SegmentArray<AnalyticSegment>();
this._analyticUnitsSet = new AnalyticUnitsSet(this._panelObject.analyticUnits); this._analyticUnitsSet = new AnalyticUnitsSet([]);
this.fetchAnalyticUnits();
this._thresholds = []; this._thresholds = [];
this.updateThresholds(); this.updateThresholds();
} }
@ -99,10 +99,10 @@ export class AnalyticController {
} }
} }
async saveNew(metric: MetricExpanded, datasource: DatasourceRequest, panelUrl: string) { async saveNew(metric: MetricExpanded, datasource: DatasourceRequest) {
this._savingNewAnalyticUnit = true; this._savingNewAnalyticUnit = true;
this._newAnalyticUnit.id = await this._analyticService.postNewItem( this._newAnalyticUnit.id = await this._analyticService.postNewItem(
this._newAnalyticUnit, metric, datasource, panelUrl this._newAnalyticUnit, metric, datasource, this._grafanaUrl, this._panelId
); );
if(this._newAnalyticUnit.detectorType === 'threshold') { if(this._newAnalyticUnit.detectorType === 'threshold') {
await this.saveThreshold(this._newAnalyticUnit.id); await this.saveThreshold(this._newAnalyticUnit.id);
@ -205,15 +205,17 @@ export class AnalyticController {
return this._analyticUnitsSet.items; return this._analyticUnitsSet.items;
} }
onAnalyticUnitColorChange(id: AnalyticUnitId, value: string, deleted: boolean) { async onAnalyticUnitColorChange(id: AnalyticUnitId, value: string, deleted: boolean) {
if(id === undefined) { if(id === undefined) {
throw new Error('id is undefined'); throw new Error('id is undefined');
} }
const analyticUnit = this._analyticUnitsSet.byId(id);
if(deleted) { if(deleted) {
this._analyticUnitsSet.byId(id).deletedColor = value; analyticUnit.deletedColor = value;
} else { } else {
this._analyticUnitsSet.byId(id).labeledColor = value; analyticUnit.labeledColor = value;
} }
await this.saveAnalyticUnit(analyticUnit);
} }
fetchAnalyticUnitsStatuses() { fetchAnalyticUnitsStatuses() {
@ -366,23 +368,39 @@ export class AnalyticController {
if(id === this._selectedAnalyticUnitId) { if(id === this._selectedAnalyticUnitId) {
this.dropLabeling(); this.dropLabeling();
} }
this._analyticUnitsSet.removeItem(id);
if(!silent) { if(!silent) {
await this._analyticService.removeAnalyticUnit(id); await this._analyticService.removeAnalyticUnit(id);
} }
this._analyticUnitsSet.removeItem(id);
} }
async toggleAnalyticUnitAlert(analyticUnit: AnalyticUnit): Promise<void> { async toggleAnalyticUnitAlert(analyticUnit: AnalyticUnit): Promise<void> {
analyticUnit.alert = analyticUnit.alert ? true : false; analyticUnit.alert = analyticUnit.alert ? true : false;
// TODO: saveAnalyticUnit instead of specific method
await this._analyticService.setAnalyticUnitAlert(analyticUnit); await this._analyticService.setAnalyticUnitAlert(analyticUnit);
} }
async fetchAnalyticUnitName(analyticUnit: AnalyticUnit): Promise<void> { async saveAnalyticUnit(analyticUnit: AnalyticUnit): Promise<void> {
let updateObj = { if(analyticUnit.id === null || analyticUnit.id === undefined) {
id: analyticUnit.id, throw new Error('Cannot save analytic unit without id');
name: analyticUnit.name }
analyticUnit.saving = true;
await this._analyticService.updateAnalyticUnit(analyticUnit.serverObject);
analyticUnit.saving = false;
}
async getAnalyticUnits(): Promise<any[]> {
if(this._analyticService === undefined) {
return [];
} }
await this._analyticService.updateAnalyticUnit(analyticUnit.id, updateObj);
return this._analyticService.getAnalyticUnits(this._panelId);
}
async fetchAnalyticUnits(): Promise<void> {
const units = await this.getAnalyticUnits();
this._analyticUnitsSet = new AnalyticUnitsSet(units);
} }
async updateThresholds(): Promise<void> { async updateThresholds(): Promise<void> {
@ -470,13 +488,14 @@ export class AnalyticController {
return this._tempIdCounted.toString(); return this._tempIdCounted.toString();
} }
public toggleVisibility(id: AnalyticUnitId, value?: boolean) { public async toggleVisibility(id: AnalyticUnitId, value?: boolean) {
var analyticUnit = this._analyticUnitsSet.byId(id); const analyticUnit = this._analyticUnitsSet.byId(id);
if(value !== undefined) { if(value !== undefined) {
analyticUnit.visible = value; analyticUnit.visible = value;
} else { } else {
analyticUnit.visible = !analyticUnit.visible; analyticUnit.visible = !analyticUnit.visible;
} }
await this.saveAnalyticUnit(analyticUnit);
} }
public onAnalyticUnitDetectorChange(analyticUnitTypes: any) { public onAnalyticUnitDetectorChange(analyticUnitTypes: any) {

45
src/panel/graph_panel/graph_ctrl.ts

@ -55,6 +55,9 @@ class GraphCtrl extends MetricsPanelCtrl {
private $graphElem: any; private $graphElem: any;
private $legendElem: any; private $legendElem: any;
private _grafanaUrl: string;
private _panelId: string;
panelDefaults = { panelDefaults = {
// datasource name, null = default datasource // datasource name, null = default datasource
datasource: null, datasource: null,
@ -157,6 +160,17 @@ class GraphCtrl extends MetricsPanelCtrl {
// because of https://github.com/hastic/hastic-grafana-app/issues/162 // because of https://github.com/hastic/hastic-grafana-app/issues/162
this.events.on('init-edit-mode', this.onInitEditMode.bind(this)); this.events.on('init-edit-mode', this.onInitEditMode.bind(this));
const grafanaUrlRegex = /^(.+)\/d/;
const parsedUrl = window.location.href.match(grafanaUrlRegex);
if(parsedUrl !== null) {
this._grafanaUrl = parsedUrl[1];
} else {
throw new Error('Cannot parse grafana url');
}
this._panelId = `${this.dashboard.uid}/${this.panel.id}`;
} }
rebindKeys() { rebindKeys() {
@ -305,10 +319,10 @@ class GraphCtrl extends MetricsPanelCtrl {
delete this.analyticService; delete this.analyticService;
} else { } else {
this.analyticService = new AnalyticService(hasticDatasource.url, this.$http); this.analyticService = new AnalyticService(hasticDatasource.url, this.$http);
this.runDatasourceConnectivityCheck(); this.runDatasourceConnectivityCheck();
} }
this.analyticsController = new AnalyticController(this.panel, this.events, this.analyticService); this.analyticsController = new AnalyticController(this._grafanaUrl, this._panelId, this.panel, this.events, this.analyticService);
this.analyticsController.fetchAnalyticUnitsStatuses(); this.analyticsController.fetchAnalyticUnitsStatuses();
this._updatePanelInfo(); this._updatePanelInfo();
@ -559,15 +573,11 @@ class GraphCtrl extends MetricsPanelCtrl {
async saveNew() { async saveNew() {
try { try {
const panelId = this.panel.id;
const panelUrl = window.location.origin + window.location.pathname + `?panelId=${panelId}`;
const datasource = await this._getDatasourceRequest(); const datasource = await this._getDatasourceRequest();
await this.analyticsController.saveNew( await this.analyticsController.saveNew(
new MetricExpanded(this.panel.datasource, this.panel.targets), new MetricExpanded(this.panel.datasource, this.panel.targets),
datasource, datasource
panelUrl
); );
} catch(e) { } catch(e) {
appEvents.emit( appEvents.emit(
@ -582,20 +592,21 @@ class GraphCtrl extends MetricsPanelCtrl {
this.render(this.seriesList); this.render(this.seriesList);
} }
onAnalyticUnitAlertChange(analyticUnit: AnalyticUnit) { async onAnalyticUnitAlertChange(analyticUnit: AnalyticUnit) {
this.analyticsController.toggleAnalyticUnitAlert(analyticUnit); await this.analyticsController.toggleAnalyticUnitAlert(analyticUnit);
} }
onAnalyticUnitNameChange(analyticUnit: AnalyticUnit) { async onAnalyticUnitNameChange(analyticUnit: AnalyticUnit) {
this.analyticsController.fetchAnalyticUnitName(analyticUnit); await this.analyticsController.saveAnalyticUnit(analyticUnit);
this.refresh();
} }
onColorChange(id: AnalyticUnitId, deleted: boolean, value: string) { async onColorChange(id: AnalyticUnitId, deleted: boolean, value: string) {
if(id === undefined) { if(id === undefined) {
throw new Error('id is undefined'); throw new Error('id is undefined');
} }
this.analyticsController.onAnalyticUnitColorChange(id, value, deleted); await this.analyticsController.onAnalyticUnitColorChange(id, value, deleted);
this.render(); this.refresh();
} }
async onRemove(id: AnalyticUnitId) { async onRemove(id: AnalyticUnitId) {
@ -603,7 +614,7 @@ class GraphCtrl extends MetricsPanelCtrl {
throw new Error('id is undefined'); throw new Error('id is undefined');
} }
await this.analyticsController.removeAnalyticUnit(id); await this.analyticsController.removeAnalyticUnit(id);
this.render(); this.refresh();
} }
onCancelLabeling(id: AnalyticUnitId) { onCancelLabeling(id: AnalyticUnitId) {
@ -650,7 +661,7 @@ class GraphCtrl extends MetricsPanelCtrl {
onToggleVisibility(id: AnalyticUnitId) { onToggleVisibility(id: AnalyticUnitId) {
this.analyticsController.toggleVisibility(id); this.analyticsController.toggleVisibility(id);
this.render(); this.refresh();
} }
private async _updatePanelInfo() { private async _updatePanelInfo() {
@ -658,7 +669,7 @@ class GraphCtrl extends MetricsPanelCtrl {
if(this.panel.datasource) { if(this.panel.datasource) {
datasource = await this._getDatasourceByName(this.panel.datasource); datasource = await this._getDatasourceByName(this.panel.datasource);
} }
const hasticDatasource = this.getHasticDatasource(); const hasticDatasource = this.getHasticDatasource();
let grafanaVersion = 'unknown'; let grafanaVersion = 'unknown';

76
src/panel/graph_panel/models/analytic_unit.ts

@ -7,6 +7,11 @@ import { ANALYTIC_UNIT_COLORS, DEFAULT_DELETED_SEGMENT_COLOR } from '../colors';
import _ from 'lodash'; import _ from 'lodash';
export enum DetectorType {
PATTERN = 'pattern',
THRESHOLD = 'threshold'
};
export enum LabelingMode { export enum LabelingMode {
LABELING = 'LABELING', LABELING = 'LABELING',
UNLABELING = 'UNLABELING', UNLABELING = 'UNLABELING',
@ -22,7 +27,7 @@ export type AnalyticUnitId = string;
export class AnalyticSegment extends Segment { export class AnalyticSegment extends Segment {
constructor(public labeled: boolean, id: SegmentId, from: number, to: number, public deleted = false) { constructor(public labeled: boolean, id: SegmentId, from: number, to: number, public deleted = false) {
super(id, from, to); super(id, from, to);
if(!_.isBoolean(labeled)) { if(!_.isBoolean(this.labeled)) {
throw new Error('labeled value is not boolean'); throw new Error('labeled value is not boolean');
} }
} }
@ -37,43 +42,44 @@ export class AnalyticUnit {
private _status: string; private _status: string;
private _error: string; private _error: string;
constructor(private _panelObject?: any) { constructor(private _serverObject?: any) {
if(_panelObject === undefined) { const defaults = {
this._panelObject = {};
}
_.defaults(this._panelObject, {
name: 'AnalyticUnitName', name: 'AnalyticUnitName',
labeledColor: ANALYTIC_UNIT_COLORS[0], labeledColor: ANALYTIC_UNIT_COLORS[0],
deletedColor: DEFAULT_DELETED_SEGMENT_COLOR, deletedColor: DEFAULT_DELETED_SEGMENT_COLOR,
detectorType: 'pattern', detectorType: DetectorType.PATTERN,
type: 'GENERAL', type: 'GENERAL',
alert: false alert: false,
}); id: null,
} visible: true
}
get id(): AnalyticUnitId { return this._panelObject.id; } if(_serverObject === undefined) {
set id(value: AnalyticUnitId) { this._panelObject.id = value; } this._serverObject = defaults;
}
_.defaults(this._serverObject, defaults);
}
set name(value: string) { this._panelObject.name = value; } get id(): AnalyticUnitId { return this._serverObject.id; }
get name(): string { return this._panelObject.name; } set id(value: AnalyticUnitId) { this._serverObject.id = value; }
set detectorType(value: string) { this._panelObject.detectorType = value; } set name(value: string) { this._serverObject.name = value; }
get detectorType(): string { return this._panelObject.detectorType; } get name(): string { return this._serverObject.name; }
set type(value: string) { this._panelObject.type = value; } set detectorType(value: DetectorType) { this._serverObject.detectorType = value; }
get type(): string { return this._panelObject.type; } get detectorType(): DetectorType { return this._serverObject.detectorType; }
set confidence(value: number) { this._panelObject.confidence = value; } set type(value: string) { this._serverObject.type = value; }
get confidence(): number { return this._panelObject.confidence; } get type(): string { return this._serverObject.type; }
set labeledColor(value: string) { this._panelObject.labeledColor = value; } set labeledColor(value: string) { this._serverObject.labeledColor = value; }
get labeledColor(): string { return this._panelObject.labeledColor; } get labeledColor(): string { return this._serverObject.labeledColor; }
set deletedColor(value: string) { this._panelObject.deletedColor = value; } set deletedColor(value: string) { this._serverObject.deletedColor = value; }
get deletedColor(): string { return this._panelObject.deletedColor; } get deletedColor(): string { return this._serverObject.deletedColor; }
set alert(value: boolean) { this._panelObject.alert = value; } set alert(value: boolean) { this._serverObject.alert = value; }
get alert(): boolean { return this._panelObject.alert; } get alert(): boolean { return this._serverObject.alert; }
get selected(): boolean { return this._selected; } get selected(): boolean { return this._selected; }
set selected(value: boolean) { this._selected = value; } set selected(value: boolean) { this._selected = value; }
@ -85,10 +91,10 @@ export class AnalyticUnit {
set saving(value: boolean) { this._saving = value; } set saving(value: boolean) { this._saving = value; }
get visible(): boolean { get visible(): boolean {
return (this._panelObject.visible === undefined) ? true : this._panelObject.visible return (this._serverObject.visible === undefined) ? true : this._serverObject.visible
} }
set visible(value: boolean) { set visible(value: boolean) {
this._panelObject.visible = value; this._serverObject.visible = value;
} }
addLabeledSegment(segment: Segment, deleted: boolean): AnalyticSegment { addLabeledSegment(segment: Segment, deleted: boolean): AnalyticSegment {
@ -134,7 +140,7 @@ export class AnalyticUnit {
return true; return true;
} }
get panelObject() { return this._panelObject; } get serverObject() { return this._serverObject; }
} }
@ -143,26 +149,26 @@ export class AnalyticUnitsSet {
private _mapIdIndex: Map<AnalyticUnitId, number>; private _mapIdIndex: Map<AnalyticUnitId, number>;
private _items: AnalyticUnit[]; private _items: AnalyticUnit[];
constructor(private _panelObject: any[]) { constructor(private _serverObject: any[]) {
if(_panelObject === undefined) { if(_serverObject === undefined) {
throw new Error('panel object can`t be undefined'); throw new Error('server object can`t be undefined');
} }
this._mapIdIndex = new Map<AnalyticUnitId, number>(); this._mapIdIndex = new Map<AnalyticUnitId, number>();
this._items = _panelObject.map(p => new AnalyticUnit(p)); this._items = _serverObject.map(p => new AnalyticUnit(p));
this._rebuildIndex(); this._rebuildIndex();
} }
get items() { return this._items; } get items() { return this._items; }
addItem(item: AnalyticUnit) { addItem(item: AnalyticUnit) {
this._panelObject.push(item.panelObject); this._serverObject.push(item.serverObject);
this._mapIdIndex[item.id] = this._items.length; this._mapIdIndex[item.id] = this._items.length;
this._items.push(item); this._items.push(item);
} }
removeItem(id: AnalyticUnitId) { removeItem(id: AnalyticUnitId) {
var index = this._mapIdIndex[id]; var index = this._mapIdIndex[id];
this._panelObject.splice(index, 1); this._serverObject.splice(index, 1);
this._items.splice(index, 1); this._items.splice(index, 1);
this._rebuildIndex(); this._rebuildIndex();
} }

2
src/panel/graph_panel/partials/tab_analytics.html

@ -30,7 +30,7 @@
<input <input
type="text" class="gf-form-input max-width-15" type="text" class="gf-form-input max-width-15"
ng-model="analyticUnit.name" ng-model="analyticUnit.name"
ng-change="ctrl.onAnalyticUnitNameChange(analyticUnit)" ng-blur="ctrl.onAnalyticUnitNameChange(analyticUnit)"
> >
<label class="gf-form-label width-4"> Type </label> <label class="gf-form-label width-4"> Type </label>

18
src/panel/graph_panel/services/analytic_service.ts

@ -9,6 +9,7 @@ import { Threshold } from '../models/threshold';
import { appEvents } from 'grafana/app/core/core'; import { appEvents } from 'grafana/app/core/core';
export class AnalyticService { export class AnalyticService {
private _isUp: boolean = false; private _isUp: boolean = false;
@ -25,6 +26,14 @@ export class AnalyticService {
return this.get('/analyticUnits/types'); return this.get('/analyticUnits/types');
} }
async getAnalyticUnits(panelId: string) {
const resp = await this.get('/analyticUnits/units', { panelId });
if(resp === undefined) {
return [];
}
return resp.analyticUnits;
}
async getThresholds(ids: AnalyticUnitId[]) { async getThresholds(ids: AnalyticUnitId[]) {
const resp = await this.get('/threshold', { ids: ids.join(',') }); const resp = await this.get('/threshold', { ids: ids.join(',') });
if(resp === undefined) { if(resp === undefined) {
@ -41,10 +50,12 @@ export class AnalyticService {
newItem: AnalyticUnit, newItem: AnalyticUnit,
metric: MetricExpanded, metric: MetricExpanded,
datasource: DatasourceRequest, datasource: DatasourceRequest,
panelUrl: string grafanaUrl: string,
panelId: string
): Promise<AnalyticUnitId> { ): Promise<AnalyticUnitId> {
const response = await this.post('/analyticUnits', { const response = await this.post('/analyticUnits', {
panelUrl, grafanaUrl,
panelId,
type: newItem.type, type: newItem.type,
name: newItem.name, name: newItem.name,
metric: metric.toJSON(), metric: metric.toJSON(),
@ -174,8 +185,7 @@ export class AnalyticService {
}); });
} }
async updateAnalyticUnit(id: AnalyticUnitId, updateObj: any) { async updateAnalyticUnit(updateObj: any) {
updateObj.id = id;
return this.patch('/analyticUnits', updateObj); return this.patch('/analyticUnits', updateObj);
} }

10
tests/analytic_controller.jest.ts

@ -10,7 +10,7 @@ describe('AnalyticController', function () {
for (let color of ANALYTIC_UNIT_COLORS) { for (let color of ANALYTIC_UNIT_COLORS) {
analyticController.createNew(); analyticController.createNew();
expect(analyticController.newAnalyticUnit.labeledColor).toBe(color); expect(analyticController.newAnalyticUnit.labeledColor).toBe(color);
await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest, ''); await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest);
} }
}); });
@ -28,16 +28,16 @@ describe('AnalyticController', function () {
it('should set different color to newly created Analytic Unit, afer NOT last AU was deleted', async function() { it('should set different color to newly created Analytic Unit, afer NOT last AU was deleted', async function() {
let auArray = analyticController.analyticUnits; let auArray = analyticController.analyticUnits;
analyticController.createNew(); analyticController.createNew();
await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest, ''); await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest);
expect(auArray[auArray.length - 2].panelObject.labeledColor).not.toBe(auArray[auArray.length - 1].panelObject.labeledColor); expect(auArray[auArray.length - 2].serverObject.labeledColor).not.toBe(auArray[auArray.length - 1].serverObject.labeledColor);
}); });
it('should set different color to newly created Analytic Unit, after LAST AU was deleted', async function () { it('should set different color to newly created Analytic Unit, after LAST AU was deleted', async function () {
let auArray = analyticController.analyticUnits; let auArray = analyticController.analyticUnits;
auArray.splice(-1, 1); auArray.splice(-1, 1);
analyticController.createNew(); analyticController.createNew();
await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest, ''); await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest);
expect(auArray[auArray.length - 2].panelObject.labeledColor).not.toBe(auArray[auArray.length - 1].panelObject.labeledColor); expect(auArray[auArray.length - 2].serverObject.labeledColor).not.toBe(auArray[auArray.length - 1].serverObject.labeledColor);
}); });
it('should change color on choosing from palette', function () { it('should change color on choosing from palette', function () {

2
tests/setup_tests.ts

@ -25,7 +25,7 @@ analyticService.postNewItem = async function (
return Promise.resolve(id.toString()); return Promise.resolve(id.toString());
} }
export const analyticController = new AnalyticController({}, new Emitter(), analyticService); export const analyticController = new AnalyticController('http://localhost:3000', '13Qdb1jmz', {}, new Emitter(), analyticService);
jest.mock('../src/panel/graph_panel/partials/help_section.html', () => ''); jest.mock('../src/panel/graph_panel/partials/help_section.html', () => '');

Loading…
Cancel
Save