Browse Source

"Server is offline" error instead of "Unexpected error" #48 (#87)

master
sanke1 6 years ago committed by rozetko
parent
commit
ae597c4644
  1. 3
      src/controllers/analytic_controller.ts
  2. 17
      src/module.ts
  3. 280
      src/partials/tab_analytics.html
  4. 110
      src/services/analytic_service.ts

3
src/controllers/analytic_controller.ts

@ -402,6 +402,9 @@ export class AnalyticController {
return this._serverInfo;
}
public get serverStatus() {
return this._analyticService.isUp;
}
}
function addAlphaToRGB(colorString: string, alpha: number): string {

17
src/module.ts

@ -16,7 +16,6 @@ import { PanelInfo } from './models/info';
import { axesEditorComponent } from './axes_editor';
import { MetricsPanelCtrl, alertTab } from 'grafana/app/plugins/sdk';
import { BackendSrv } from 'grafana/app/core/services/backend_srv';
import { appEvents } from 'grafana/app/core/core'
import config from 'grafana/app/core/config';
@ -28,6 +27,7 @@ const BACKEND_VARIABLE_NAME = 'HASTIC_SERVER_URL';
class GraphCtrl extends MetricsPanelCtrl {
static template = template;
analyticService: AnalyticService;
hiddenSeries: any = {};
seriesList: any = [];
dataList: any = [];
@ -148,7 +148,7 @@ class GraphCtrl extends MetricsPanelCtrl {
/** @ngInject */
constructor(
$scope, $injector, private annotationsSrv,
$scope, $injector, $http, private annotationsSrv,
private keybindingSrv, private backendSrv,
private popoverSrv, private contextSrv,
private alertSrv
@ -163,11 +163,11 @@ class GraphCtrl extends MetricsPanelCtrl {
this.processor = new DataProcessor(this.panel);
var anomalyService = new AnalyticService(this.backendURL, backendSrv as BackendSrv);
this.analyticService = new AnalyticService(this.backendURL, $http, this.alertSrv);
this.runBackendConnectivityCheck();
this.analyticsController = new AnalyticController(this.panel, anomalyService, this.events);
this.analyticsController = new AnalyticController(this.panel, this.analyticService, this.events);
this.anomalyTypes = this.panel.anomalyTypes;
keybindingSrv.bind('d', this.onDKey.bind(this));
@ -224,11 +224,12 @@ class GraphCtrl extends MetricsPanelCtrl {
return;
}
var as = new AnalyticService(this.backendURL, this.backendSrv);
var isOK = await as.isBackendOk();
if(!isOK) {
let connected = await this.analyticService.isBackendOk();
if(connected) {
this.alertSrv.set(
'Can`t connect to Hastic server', `Hastic server: "${this.backendURL}"`, 'warning', 4000
'Connected to Hastic server',
`Hastic server: "${this.backendURL}"`,
'success', 4000
);
}
}

280
src/partials/tab_analytics.html

@ -1,148 +1,160 @@
<h5> Analytic Units </h5>
<div class="editor-row">
<div class="gf-form" ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits">
<label class="gf-form-label width-4"> Name </label>
<input
type="text" class="gf-form-input max-width-15"
ng-model="analyticUnit.name"
ng-disabled="true"
>
<label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper">
<select class="gf-form-input width-12"
ng-model="analyticUnit.type"
ng-options="type.value as type.name for type in ctrl.panel.analyticUnitTypes"
ng-disabled="true"
/>
</div>
<div class="gf-form-button-row" ng-if="ctrl.analyticsController.serverStatus === false">
<h5>Hastic server at "{{ctrl.backendURL}}" is not available</h5>
<button class="btn btn-inverse" ng-click="ctrl.runBackendConnectivityCheck()">
<i class="fa fa-plug"></i>
Reconnect to Hastic server
</button>
</div>
<!--
<label class="gf-form-label width-6"> Confidence </label>
<input
type="number" class="gf-form-input width-5 ng-valid ng-scope ng-empty ng-dirty ng-valid-number ng-touched"
placeholder="auto" bs-tooltip="'Override automatic decimal precision for legend and tooltips'"
data-placement="right" ng-model="ctrl.panel.decimals" ng-change="ctrl.render()" ng-model-onblur="" data-original-title="" title=""
/>
-->
<label class="gf-form-label width-6"> Color </label>
<span class="gf-form-label">
<color-picker
color="analyticUnit.color"
onChange="ctrl.onColorChange.bind(ctrl, analyticUnit.id)"
/>
</span>
<div ng-if="ctrl.analyticsController.serverStatus === true">
<h5> Analytic Units </h5>
<div class="editor-row">
<div class="gf-form" ng-repeat="analyticUnit in ctrl.analyticsController.analyticUnits">
<label class="gf-form-label" ng-style="analyticUnit.status === 'LEARNING' && { 'cursor': 'not-allowed' }">
<a class="pointer" tabindex="1"
ng-click="ctrl.onToggleLabelingMode(analyticUnit.id)"
ng-disabled="analyticUnit.status === 'LEARNING'"
>
<i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i>
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="analyticUnit.selected && !analyticUnit.deleteMode && !analyticUnit.saving"> labeling </b>
<b ng-if="analyticUnit.selected && analyticUnit.deleteMode && !analyticUnit.saving"> deleting </b>
<b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</a>
</label>
<!-- <label class="gf-form-label"> Alerts: </label>
<label
class="gf-form-label text-center"
style="width: 4rem"
ng-if="analyticUnit.alertEnabled === undefined"
bs-tooltip="'Alarting status isn`t available. Wait please.'"
>
<i class="fa fa-spinner fa-spin"></i>
</label> -->
<gf-form-switch
ng-if="analyticUnit.alertEnabled !== undefined"
on-change="ctrl.onAnomalyAlertChange(analyticUnit)"
checked="analyticUnit.alertEnabled"
style="height: 36px;"
/>
<label class="gf-form-label">
<a
ng-if="analyticUnit.visible"
ng-disabled="analyticUnit.selected"
bs-tooltip="'Hide. It`s visible now.'"
ng-click="ctrl.onToggleVisibility(analyticUnit.id)"
class="pointer"
>
<i class="fa fa-eye"></i>
</a>
<a
ng-if="!analyticUnit.visible"
ng-disabled="analyticUnit.selected"
bs-tooltip="'Show. It`s hidden now.'"
ng-click="ctrl.onToggleVisibility(analyticUnit.id)"
class="pointer"
>
<i class="fa fa-eye-slash"></i>
</a>
</label>
<label class="gf-form-label">
<a
ng-if="!analyticUnit.selected"
ng-click="ctrl.onRemove(analyticUnit.id)"
class="pointer"
<label class="gf-form-label width-4"> Name </label>
<input
type="text" class="gf-form-input max-width-15"
ng-model="analyticUnit.name"
ng-disabled="true"
>
<i class="fa fa-trash"></i>
</a>
<a
ng-if="analyticUnit.selected"
ng-click="ctrl.onCancelLabeling(analyticUnit.id)"
class="pointer"
<label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper">
<select class="gf-form-input width-12"
ng-model="analyticUnit.type"
ng-options="type.value as type.name for type in ctrl.panel.analyticUnitTypes"
ng-disabled="true"
/>
</div>
<!--
<label class="gf-form-label width-6"> Confidence </label>
<input
type="number" class="gf-form-input width-5 ng-valid ng-scope ng-empty ng-dirty ng-valid-number ng-touched"
placeholder="auto" bs-tooltip="'Override automatic decimal precision for legend and tooltips'"
data-placement="right" ng-model="ctrl.panel.decimals" ng-change="ctrl.render()" ng-model-onblur="" data-original-title="" title=""
/>
-->
<label class="gf-form-label width-6"> Color </label>
<span class="gf-form-label">
<color-picker
color="analyticUnit.color"
onChange="ctrl.onColorChange.bind(ctrl, analyticUnit.id)"
/>
</span>
<label class="gf-form-label" ng-style="analyticUnit.status === 'LEARNING' && { 'cursor': 'not-allowed' }">
<a class="pointer" tabindex="1"
ng-click="ctrl.onToggleLabelingMode(analyticUnit.id)"
ng-disabled="analyticUnit.status === 'LEARNING'"
>
<i class="fa fa-bar-chart" ng-if="!analyticUnit.saving"></i>
<i class="fa fa-spinner fa-spin" ng-if="analyticUnit.saving"></i>
<b ng-if="analyticUnit.selected && !analyticUnit.deleteMode && !analyticUnit.saving"> labeling </b>
<b ng-if="analyticUnit.selected && analyticUnit.deleteMode && !analyticUnit.saving"> deleting </b>
<b ng-if="analyticUnit.saving" ng-disabled="true"> saving... </b>
</a>
</label>
<!-- <label class="gf-form-label"> Alerts: </label>
<label
class="gf-form-label text-center"
style="width: 4rem"
ng-if="analyticUnit.alertEnabled === undefined"
bs-tooltip="'Alarting status isn`t available. Wait please.'"
>
<i class="fa fa-ban"></i>
</a>
</label>
<i class="fa fa-spinner fa-spin"></i>
</label> -->
<gf-form-switch
ng-if="analyticUnit.alertEnabled !== undefined"
on-change="ctrl.onAnomalyAlertChange(analyticUnit)"
checked="analyticUnit.alertEnabled"
style="height: 36px;"
/>
<label>
<i ng-if="analyticUnit.status === 'LEARNING'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i>
<i ng-if="analyticUnit.status === 'PENDING'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i>
<i ng-if="analyticUnit.status === 'FAILED'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + analyticUnit.error"></i>
</label>
<label class="gf-form-label">
<a
ng-if="analyticUnit.visible"
ng-disabled="analyticUnit.selected"
bs-tooltip="'Hide. It`s visible now.'"
ng-click="ctrl.onToggleVisibility(analyticUnit.id)"
class="pointer"
>
<i class="fa fa-eye"></i>
</a>
<a
ng-if="!analyticUnit.visible"
ng-disabled="analyticUnit.selected"
bs-tooltip="'Show. It`s hidden now.'"
ng-click="ctrl.onToggleVisibility(analyticUnit.id)"
class="pointer"
>
<i class="fa fa-eye-slash"></i>
</a>
</label>
<label class="gf-form-label">
<a
ng-if="!analyticUnit.selected"
ng-click="ctrl.onRemove(analyticUnit.id)"
class="pointer"
>
<i class="fa fa-trash"></i>
</a>
<a
ng-if="analyticUnit.selected"
ng-click="ctrl.onCancelLabeling(analyticUnit.id)"
class="pointer"
>
<i class="fa fa-ban"></i>
</a>
</label>
<label>
<i ng-if="analyticUnit.status === 'LEARNING'" class="grafana-tip fa fa-leanpub ng-scope" bs-tooltip="'Learning'"></i>
<i ng-if="analyticUnit.status === 'PENDING'" class="grafana-tip fa fa-list-ul ng-scope" bs-tooltip="'Pending'"></i>
<i ng-if="analyticUnit.status === 'FAILED'" class="grafana-tip fa fa-exclamation-circle ng-scope" bs-tooltip="'Error: ' + analyticUnit.error"></i>
</label>
</div>
</div>
</div>
<div class="editor-row" ng-if="ctrl.analyticsController.creatingNew">
<div class="gf-form">
<label class="gf-form-label width-4"> Name </label>
<input
type="text" class="gf-form-input max-width-15"
ng-model="ctrl.analyticsController.newAnalyticUnit.name"
>
<label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper">
<select class="gf-form-input width-12"
ng-model="ctrl.analyticsController.newAnalyticUnit.type"
ng-options="type.value as type.name for type in ctrl.panel.analyticUnitTypes"
/>
<div class="editor-row" ng-if="ctrl.analyticsController.creatingNew">
<div class="gf-form">
<label class="gf-form-label width-4"> Name </label>
<input
type="text" class="gf-form-input max-width-15"
ng-model="ctrl.analyticsController.newAnalyticUnit.name"
>
<label class="gf-form-label width-8"> Type </label>
<div class="gf-form-select-wrapper">
<select class="gf-form-input width-12"
ng-model="ctrl.analyticsController.newAnalyticUnit.type"
ng-options="type.value as type.name for type in ctrl.panel.analyticUnitTypes"
/>
</div>
<label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="ctrl.saveNew()">
<b ng-if="!ctrl.analyticsController.saving"> create </b>
<b ng-if="ctrl.analyticsController.saving" ng-disabled="true"> saving... </b>
</a>
</label>
</div>
</div>
<div class="gf-form-button-row" ng-if="!ctrl.analyticsController.creatingAnalyticUnit">
<button class="btn btn-inverse" ng-click="ctrl.createNew()">
<i class="fa fa-plus"></i>
Add Analytic Unit
</button>
<label class="gf-form-label">
<a class="pointer" tabindex="1" ng-click="ctrl.saveNew()">
<b ng-if="!ctrl.analyticsController.saving"> create </b>
<b ng-if="ctrl.analyticsController.saving" ng-disabled="true"> saving... </b>
</a>
</label>
</div>
</div>
<div class="gf-form-button-row" ng-if="!ctrl.analyticsController.creatingAnalyticUnit">
<button class="btn btn-inverse" ng-click="ctrl.createNew()">
<i class="fa fa-plus"></i>
Add Analytic Unit
</button>
</div>

110
src/services/analytic_service.ts

@ -5,46 +5,41 @@ 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';
export class AnalyticService {
constructor(private _backendURL: string, private _backendSrv: BackendSrv) {
private _isUp = false;
constructor(private _backendURL: string, private $http, private alertSrv) {
this.isBackendOk();
}
async postNewItem(
metric: MetricExpanded, datasourceRequest: DatasourceRequest,
newItem: AnalyticUnit, panelId: number
): Promise<AnalyticUnitId> {
let datasource = await this._backendSrv.get(`/api/datasources/name/${metric.datasource}`);
let datasource = await this.get(`/api/datasources/name/${metric.datasource}`);
datasourceRequest.type = datasource.type;
return this._backendSrv.post(
this._backendURL + '/analyticUnits',
{
panelUrl: window.location.origin + window.location.pathname + `?panelId=${panelId}&fullscreen`,
type: newItem.type,
name: newItem.name,
metric: metric.toJSON(),
datasource: datasourceRequest
}
).then(res => res.id as AnalyticUnitId);
};
const response = await this.post(this._backendURL + '/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<boolean> {
try {
var data = await this._backendSrv.get(this._backendURL);
// TODO: check version
return true;
} catch(e) {
return false;
}
await this.get(this._backendURL);
return this._isUp;
}
async updateSegments(
id: AnalyticUnitId, addedSegments: SegmentsSet<Segment>, removedSegments: SegmentsSet<Segment>
): Promise<SegmentId[]> {
const getJSONs = (segs: SegmentsSet<Segment>) => segs.getSegments().map(segment => ({
from: segment.from,
to: segment.to
@ -56,11 +51,10 @@ export class AnalyticService {
removedSegments: removedSegments.getSegments().map(s => s.id)
};
var data = await this._backendSrv.patch(this._backendURL + '/segments', payload);
var data = await this.patch(this._backendURL + '/segments', payload);
if(data.addedIds === undefined) {
throw new Error('Server didn`t send addedIds');
}
return data.addedIds as SegmentId[];
}
@ -75,7 +69,7 @@ export class AnalyticService {
if(to !== undefined) {
payload['to'] = to;
}
var data = await this._backendSrv.get(this._backendURL + '/segments', payload);
var data = await this.get(this._backendURL + '/segments', payload);
if(data.segments === undefined) {
throw new Error('Server didn`t return segments array');
}
@ -88,9 +82,7 @@ export class AnalyticService {
throw new Error('id is undefined');
}
let statusCheck = async () => {
var data = await this._backendSrv.get(
this._backendURL + '/analyticUnits/status', { id }
);
var data = await this.get(this._backendURL + '/analyticUnits/status', { id });
return data;
}
@ -102,34 +94,29 @@ export class AnalyticService {
yield await statusCheck();
await timeout();
}
}
async getAlertEnabled(id: AnalyticUnitId): Promise<boolean> {
if(id === undefined) {
throw new Error('id is undefined');
}
var data = await this._backendSrv.get(
this._backendURL + '/alerts', { id }
);
var data = await this.get(this._backendURL + '/alerts', { id });
if(data.enabled === undefined) {
throw new Error('Server didn`t return "enabled"');
}
return data.enabled as boolean;
}
async setAlertEnabled(id: AnalyticUnitId, enabled: boolean): Promise<void> {
if(id === undefined) {
throw new Error('id is undefined');
}
return this._backendSrv.post(
this._backendURL + '/alerts', { id, enabled }
);
return await this.post(this._backendURL + '/alerts', { id, enabled });
}
async getServerInfo(): Promise<ServerInfo> {
let data = await this._backendSrv.get(this._backendURL);
let data = await this.get(this._backendURL);
console.log(data);
return {
nodeVersion: data.nodeVersion,
packageVersion: data.packageVersion,
@ -142,4 +129,51 @@ export class AnalyticService {
};
}
private async get(url, params?) {
try {
let response = await this.$http({ method: 'GET', url, params });
this._isUp = true;
return response.data;
} catch(error) {
this.displayConnectionAlert();
console.error(error);
this._isUp = false;
}
}
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 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 displayConnectionAlert() {
this.alertSrv.set(
'No connection to Hastic server',
`Hastic server: "${this._backendURL}"`,
'warning', 4000
);
}
public get isUp() {
return this._isUp;
}
}

Loading…
Cancel
Save