Browse Source

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

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

45
src/panel/graph_panel/graph_ctrl.ts

@ -55,6 +55,9 @@ class GraphCtrl extends MetricsPanelCtrl {
private $graphElem: any;
private $legendElem: any;
private _grafanaUrl: string;
private _panelId: string;
panelDefaults = {
// datasource name, null = default datasource
datasource: null,
@ -157,6 +160,17 @@ class GraphCtrl extends MetricsPanelCtrl {
// because of https://github.com/hastic/hastic-grafana-app/issues/162
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() {
@ -305,10 +319,10 @@ class GraphCtrl extends MetricsPanelCtrl {
delete this.analyticService;
} else {
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._updatePanelInfo();
@ -559,15 +573,11 @@ class GraphCtrl extends MetricsPanelCtrl {
async saveNew() {
try {
const panelId = this.panel.id;
const panelUrl = window.location.origin + window.location.pathname + `?panelId=${panelId}`;
const datasource = await this._getDatasourceRequest();
await this.analyticsController.saveNew(
new MetricExpanded(this.panel.datasource, this.panel.targets),
datasource,
panelUrl
datasource
);
} catch(e) {
appEvents.emit(
@ -582,20 +592,21 @@ class GraphCtrl extends MetricsPanelCtrl {
this.render(this.seriesList);
}
onAnalyticUnitAlertChange(analyticUnit: AnalyticUnit) {
this.analyticsController.toggleAnalyticUnitAlert(analyticUnit);
async onAnalyticUnitAlertChange(analyticUnit: AnalyticUnit) {
await this.analyticsController.toggleAnalyticUnitAlert(analyticUnit);
}
onAnalyticUnitNameChange(analyticUnit: AnalyticUnit) {
this.analyticsController.fetchAnalyticUnitName(analyticUnit);
async onAnalyticUnitNameChange(analyticUnit: 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) {
throw new Error('id is undefined');
}
this.analyticsController.onAnalyticUnitColorChange(id, value, deleted);
this.render();
await this.analyticsController.onAnalyticUnitColorChange(id, value, deleted);
this.refresh();
}
async onRemove(id: AnalyticUnitId) {
@ -603,7 +614,7 @@ class GraphCtrl extends MetricsPanelCtrl {
throw new Error('id is undefined');
}
await this.analyticsController.removeAnalyticUnit(id);
this.render();
this.refresh();
}
onCancelLabeling(id: AnalyticUnitId) {
@ -650,7 +661,7 @@ class GraphCtrl extends MetricsPanelCtrl {
onToggleVisibility(id: AnalyticUnitId) {
this.analyticsController.toggleVisibility(id);
this.render();
this.refresh();
}
private async _updatePanelInfo() {
@ -658,7 +669,7 @@ class GraphCtrl extends MetricsPanelCtrl {
if(this.panel.datasource) {
datasource = await this._getDatasourceByName(this.panel.datasource);
}
const hasticDatasource = this.getHasticDatasource();
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';
export enum DetectorType {
PATTERN = 'pattern',
THRESHOLD = 'threshold'
};
export enum LabelingMode {
LABELING = 'LABELING',
UNLABELING = 'UNLABELING',
@ -22,7 +27,7 @@ export type AnalyticUnitId = string;
export class AnalyticSegment extends Segment {
constructor(public labeled: boolean, id: SegmentId, from: number, to: number, public deleted = false) {
super(id, from, to);
if(!_.isBoolean(labeled)) {
if(!_.isBoolean(this.labeled)) {
throw new Error('labeled value is not boolean');
}
}
@ -37,43 +42,44 @@ export class AnalyticUnit {
private _status: string;
private _error: string;
constructor(private _panelObject?: any) {
if(_panelObject === undefined) {
this._panelObject = {};
}
_.defaults(this._panelObject, {
constructor(private _serverObject?: any) {
const defaults = {
name: 'AnalyticUnitName',
labeledColor: ANALYTIC_UNIT_COLORS[0],
deletedColor: DEFAULT_DELETED_SEGMENT_COLOR,
detectorType: 'pattern',
detectorType: DetectorType.PATTERN,
type: 'GENERAL',
alert: false
});
}
alert: false,
id: null,
visible: true
}
get id(): AnalyticUnitId { return this._panelObject.id; }
set id(value: AnalyticUnitId) { this._panelObject.id = value; }
if(_serverObject === undefined) {
this._serverObject = defaults;
}
_.defaults(this._serverObject, defaults);
}
set name(value: string) { this._panelObject.name = value; }
get name(): string { return this._panelObject.name; }
get id(): AnalyticUnitId { return this._serverObject.id; }
set id(value: AnalyticUnitId) { this._serverObject.id = value; }
set detectorType(value: string) { this._panelObject.detectorType = value; }
get detectorType(): string { return this._panelObject.detectorType; }
set name(value: string) { this._serverObject.name = value; }
get name(): string { return this._serverObject.name; }
set type(value: string) { this._panelObject.type = value; }
get type(): string { return this._panelObject.type; }
set detectorType(value: DetectorType) { this._serverObject.detectorType = value; }
get detectorType(): DetectorType { return this._serverObject.detectorType; }
set confidence(value: number) { this._panelObject.confidence = value; }
get confidence(): number { return this._panelObject.confidence; }
set type(value: string) { this._serverObject.type = value; }
get type(): string { return this._serverObject.type; }
set labeledColor(value: string) { this._panelObject.labeledColor = value; }
get labeledColor(): string { return this._panelObject.labeledColor; }
set labeledColor(value: string) { this._serverObject.labeledColor = value; }
get labeledColor(): string { return this._serverObject.labeledColor; }
set deletedColor(value: string) { this._panelObject.deletedColor = value; }
get deletedColor(): string { return this._panelObject.deletedColor; }
set deletedColor(value: string) { this._serverObject.deletedColor = value; }
get deletedColor(): string { return this._serverObject.deletedColor; }
set alert(value: boolean) { this._panelObject.alert = value; }
get alert(): boolean { return this._panelObject.alert; }
set alert(value: boolean) { this._serverObject.alert = value; }
get alert(): boolean { return this._serverObject.alert; }
get selected(): boolean { return this._selected; }
set selected(value: boolean) { this._selected = value; }
@ -85,10 +91,10 @@ export class AnalyticUnit {
set saving(value: boolean) { this._saving = value; }
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) {
this._panelObject.visible = value;
this._serverObject.visible = value;
}
addLabeledSegment(segment: Segment, deleted: boolean): AnalyticSegment {
@ -134,7 +140,7 @@ export class AnalyticUnit {
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 _items: AnalyticUnit[];
constructor(private _panelObject: any[]) {
if(_panelObject === undefined) {
throw new Error('panel object can`t be undefined');
constructor(private _serverObject: any[]) {
if(_serverObject === undefined) {
throw new Error('server object can`t be undefined');
}
this._mapIdIndex = new Map<AnalyticUnitId, number>();
this._items = _panelObject.map(p => new AnalyticUnit(p));
this._items = _serverObject.map(p => new AnalyticUnit(p));
this._rebuildIndex();
}
get items() { return this._items; }
addItem(item: AnalyticUnit) {
this._panelObject.push(item.panelObject);
this._serverObject.push(item.serverObject);
this._mapIdIndex[item.id] = this._items.length;
this._items.push(item);
}
removeItem(id: AnalyticUnitId) {
var index = this._mapIdIndex[id];
this._panelObject.splice(index, 1);
this._serverObject.splice(index, 1);
this._items.splice(index, 1);
this._rebuildIndex();
}

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

@ -30,7 +30,7 @@
<input
type="text" class="gf-form-input max-width-15"
ng-model="analyticUnit.name"
ng-change="ctrl.onAnalyticUnitNameChange(analyticUnit)"
ng-blur="ctrl.onAnalyticUnitNameChange(analyticUnit)"
>
<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';
export class AnalyticService {
private _isUp: boolean = false;
@ -25,6 +26,14 @@ export class AnalyticService {
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[]) {
const resp = await this.get('/threshold', { ids: ids.join(',') });
if(resp === undefined) {
@ -41,10 +50,12 @@ export class AnalyticService {
newItem: AnalyticUnit,
metric: MetricExpanded,
datasource: DatasourceRequest,
panelUrl: string
grafanaUrl: string,
panelId: string
): Promise<AnalyticUnitId> {
const response = await this.post('/analyticUnits', {
panelUrl,
grafanaUrl,
panelId,
type: newItem.type,
name: newItem.name,
metric: metric.toJSON(),
@ -174,8 +185,7 @@ export class AnalyticService {
});
}
async updateAnalyticUnit(id: AnalyticUnitId, updateObj: any) {
updateObj.id = id;
async updateAnalyticUnit(updateObj: any) {
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) {
analyticController.createNew();
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() {
let auArray = analyticController.analyticUnits;
analyticController.createNew();
await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest, '');
expect(auArray[auArray.length - 2].panelObject.labeledColor).not.toBe(auArray[auArray.length - 1].panelObject.labeledColor);
await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest);
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 () {
let auArray = analyticController.analyticUnits;
auArray.splice(-1, 1);
analyticController.createNew();
await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest, '');
expect(auArray[auArray.length - 2].panelObject.labeledColor).not.toBe(auArray[auArray.length - 1].panelObject.labeledColor);
await analyticController.saveNew({} as MetricExpanded, {} as DatasourceRequest);
expect(auArray[auArray.length - 2].serverObject.labeledColor).not.toBe(auArray[auArray.length - 1].serverObject.labeledColor);
});
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());
}
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', () => '');

Loading…
Cancel
Save