Browse Source

Export analytic units (#833)

pull/1/head
rozetko 5 years ago committed by GitHub
parent
commit
a364e66899
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      server/src/index.ts
  2. 12
      server/src/models/analytic_unit_cache_model.ts
  3. 21
      server/src/models/analytic_units/analytic_unit_model.ts
  4. 6
      server/src/models/analytic_units/index.ts
  5. 24
      server/src/models/analytic_units/types.ts
  6. 2
      server/src/models/analytic_units/utils.ts
  7. 10
      server/src/models/detection_model.ts
  8. 8
      server/src/models/segment_model.ts
  9. 18
      server/src/routes/panel_router.ts
  10. 29
      server/src/services/export_service.ts
  11. 7
      server/src/types.ts

2
server/src/index.ts

@ -2,6 +2,7 @@ import { router as analyticUnitsRouter } from './routes/analytic_units_router';
import { router as segmentsRouter } from './routes/segments_router'; import { router as segmentsRouter } from './routes/segments_router';
import { router as dataRouter } from './routes/data_router'; import { router as dataRouter } from './routes/data_router';
import { router as detectionsRouter } from './routes/detections_router'; import { router as detectionsRouter } from './routes/detections_router';
import { router as panelRouter } from './routes/panel_router';
import * as AnalyticsController from './controllers/analytics_controller'; import * as AnalyticsController from './controllers/analytics_controller';
@ -57,6 +58,7 @@ async function init() {
rootRouter.use('/segments', segmentsRouter.routes(), segmentsRouter.allowedMethods()); rootRouter.use('/segments', segmentsRouter.routes(), segmentsRouter.allowedMethods());
rootRouter.use('/query', dataRouter.routes(), dataRouter.allowedMethods()); rootRouter.use('/query', dataRouter.routes(), dataRouter.allowedMethods());
rootRouter.use('/detections', detectionsRouter.routes(), detectionsRouter.allowedMethods()); rootRouter.use('/detections', detectionsRouter.routes(), detectionsRouter.allowedMethods());
rootRouter.use('/panels', panelRouter.routes(), panelRouter.allowedMethods());
rootRouter.get('/', async (ctx) => { rootRouter.get('/', async (ctx) => {
const activeWebhooks = await AnalyticsController.getActiveWebhooks(); const activeWebhooks = await AnalyticsController.getActiveWebhooks();

12
server/src/models/analytic_unit_cache_model.ts

@ -8,6 +8,10 @@ const db = makeDBQ(Collection.ANALYTIC_UNIT_CACHES);
// TODO: count milliseconds in index from dataset // TODO: count milliseconds in index from dataset
const MILLISECONDS_IN_INDEX = 60000; const MILLISECONDS_IN_INDEX = 60000;
type FindManyQuery = {
_id: { $in: AnalyticUnitId[] }
};
export class AnalyticUnitCache { export class AnalyticUnitCache {
public constructor( public constructor(
public id: AnalyticUnitId, public id: AnalyticUnitId,
@ -65,6 +69,14 @@ export async function findById(id: AnalyticUnitId): Promise<AnalyticUnitCache |
return AnalyticUnitCache.fromObject(obj); return AnalyticUnitCache.fromObject(obj);
} }
export async function findMany(query: FindManyQuery): Promise<AnalyticUnitCache[]> {
let caches = await db.findMany(query);
if(caches === null) {
return [];
}
return caches.map(cache => AnalyticUnitCache.fromObject(cache));
}
export async function create(id: AnalyticUnitId): Promise<AnalyticUnitId> { export async function create(id: AnalyticUnitId): Promise<AnalyticUnitId> {
let cache = new AnalyticUnitCache(id); let cache = new AnalyticUnitCache(id);
return db.insertOne(cache.toObject()); return db.insertOne(cache.toObject());

21
server/src/models/analytic_units/analytic_unit_model.ts

@ -1,7 +1,12 @@
import { AnalyticUnitId, AnalyticUnitStatus, DetectorType } from './types'; import {
AnalyticUnitId, AnalyticUnitStatus, DetectorType,
SerializedAnalyticUnit, SerializedPanelAnalyticUnit
} from './types';
import { Metric } from 'grafana-datasource-kit'; import { Metric } from 'grafana-datasource-kit';
import * as _ from 'lodash';
export abstract class AnalyticUnit { export abstract class AnalyticUnit {
public learningAfterUpdateRequired = false; public learningAfterUpdateRequired = false;
@ -38,7 +43,7 @@ export abstract class AnalyticUnit {
} }
} }
public toObject() { public toObject(): SerializedAnalyticUnit {
let metric; let metric;
if(this.metric !== undefined) { if(this.metric !== undefined) {
metric = this.metric.toObject(); metric = this.metric.toObject();
@ -63,7 +68,7 @@ export abstract class AnalyticUnit {
}; };
} }
public toPanelObject() { public toPanelObject(): SerializedPanelAnalyticUnit {
return { return {
id: this.id, id: this.id,
name: this.name, name: this.name,
@ -77,6 +82,16 @@ export abstract class AnalyticUnit {
}; };
} }
public toTemplate(): SerializedAnalyticUnit {
const obj = _.cloneDeep(this.toObject());
obj.grafanaUrl = '${GRAFANA_URL}';
obj.panelId = '${PANEL_ID}';
obj.metric.datasource.url = '${DATASOURCE_URL}';
return obj;
}
get analyticProps () { get analyticProps () {
return {}; return {};
} }

6
server/src/models/analytic_units/index.ts

@ -1,5 +1,8 @@
import { createAnalyticUnitFromObject } from './utils'; import { createAnalyticUnitFromObject } from './utils';
import { AnalyticUnitId, AnalyticUnitStatus, DetectorType, ANALYTIC_UNIT_TYPES } from './types'; import {
AnalyticUnitId, AnalyticUnitStatus, DetectorType, ANALYTIC_UNIT_TYPES,
SerializedAnalyticUnit, SerializedPanelAnalyticUnit
} from './types';
import { AnalyticUnit } from './analytic_unit_model'; import { AnalyticUnit } from './analytic_unit_model';
import { PatternAnalyticUnit } from './pattern_analytic_unit_model'; import { PatternAnalyticUnit } from './pattern_analytic_unit_model';
import { ThresholdAnalyticUnit, Condition } from './threshold_analytic_unit_model'; import { ThresholdAnalyticUnit, Condition } from './threshold_analytic_unit_model';
@ -19,6 +22,7 @@ import {
export { export {
AnalyticUnit, PatternAnalyticUnit, ThresholdAnalyticUnit, AnomalyAnalyticUnit, AnalyticUnit, PatternAnalyticUnit, ThresholdAnalyticUnit, AnomalyAnalyticUnit,
SerializedAnalyticUnit, SerializedPanelAnalyticUnit,
AnalyticUnitId, AnalyticUnitStatus, Bound, DetectorType, ANALYTIC_UNIT_TYPES, AnalyticUnitId, AnalyticUnitStatus, Bound, DetectorType, ANALYTIC_UNIT_TYPES,
createAnalyticUnitFromObject, Condition, createAnalyticUnitFromObject, Condition,
findById, findMany, findById, findMany,

24
server/src/models/analytic_units/types.ts

@ -1,3 +1,5 @@
import { Omit } from '../../types';
import { Metric } from 'grafana-datasource-kit'; import { Metric } from 'grafana-datasource-kit';
@ -71,3 +73,25 @@ export enum DetectorType {
ANOMALY = 'anomaly', ANOMALY = 'anomaly',
THRESHOLD = 'threshold' THRESHOLD = 'threshold'
}; };
export type SerializedPanelAnalyticUnit = {
id: AnalyticUnitId;
name: string;
type: string;
alert: boolean;
labeledColor?: string;
deletedColor?: string;
detectorType?: DetectorType;
visible?: boolean;
collapsed?: boolean;
}
export type SerializedAnalyticUnit = Omit<SerializedPanelAnalyticUnit, 'id'> & {
grafanaUrl: string;
panelId: string;
metric?: Metric;
_id?: AnalyticUnitId;
lastDetectionTime?: number;
status?: AnalyticUnitStatus;
error?: string;
}

2
server/src/models/analytic_units/utils.ts

@ -1,4 +1,4 @@
import { DetectorType, ANALYTIC_UNIT_TYPES } from './types'; import { DetectorType } from './types';
import { AnalyticUnit } from './analytic_unit_model'; import { AnalyticUnit } from './analytic_unit_model';
import { PatternAnalyticUnit } from './pattern_analytic_unit_model'; import { PatternAnalyticUnit } from './pattern_analytic_unit_model';
import { AnomalyAnalyticUnit } from './anomaly_analytic_unit_model'; import { AnomalyAnalyticUnit } from './anomaly_analytic_unit_model';

10
server/src/models/detection_model.ts

@ -106,6 +106,16 @@ export async function findMany(id: AnalyticUnitId, query?: FindManyQuery): Promi
return spans.map(DetectionSpan.fromObject); return spans.map(DetectionSpan.fromObject);
} }
// TODO: maybe it could have a better name
export async function findByAnalyticUnitIds(analyticUnitIds: AnalyticUnitId[]): Promise<DetectionSpan[]> {
const spans = await db.findMany({ analyticUnitId: { $in: analyticUnitIds } });
if(spans === null) {
return [];
}
return spans.map(DetectionSpan.fromObject);
}
export async function getIntersectedSpans( export async function getIntersectedSpans(
analyticUnitId: AnalyticUnitId, analyticUnitId: AnalyticUnitId,
from: number, from: number,

8
server/src/models/segment_model.ts

@ -106,6 +106,14 @@ export async function findMany(id: AnalyticUnitId, query: FindManyQuery): Promis
return segs.map(Segment.fromObject); return segs.map(Segment.fromObject);
} }
export async function findByAnalyticUnitIds(analyticUnitIds: AnalyticUnitId[]): Promise<any[]> {
const segments = await db.findMany({ analyticUnitId: { $in: analyticUnitIds } });
if(segments === null) {
return [];
}
return segments.map(Segment.fromObject);
}
/** /**
* If `from` and `to` are defined: @returns segments intersected with `[from; to]` * If `from` and `to` are defined: @returns segments intersected with `[from; to]`

18
server/src/routes/panel_router.ts

@ -0,0 +1,18 @@
import { exportPanel } from '../services/export_service';
import * as Router from 'koa-router';
async function getPanelTemplate(ctx: Router.IRouterContext) {
let panelId = ctx.request.query.panelId;
if(panelId === undefined) {
throw new Error('Cannot export analytic units with undefined panelId');
}
const json = await exportPanel(panelId);
ctx.response.body = json;
}
export var router = new Router();
router.get('/template', getPanelTemplate);

29
server/src/services/export_service.ts

@ -0,0 +1,29 @@
import * as AnalyticUnit from '../models/analytic_units';
import * as AnalyticUnitCache from '../models/analytic_unit_cache_model';
import * as DetectionSpan from '../models/detection_model';
import * as Segment from '../models/segment_model';
export async function exportPanel(panelId: string): Promise<{
analyticUnitTemplates: any[],
caches: AnalyticUnitCache.AnalyticUnitCache[],
detectionSpans: DetectionSpan.DetectionSpan[],
segments: Segment.Segment[]
}> {
const analyticUnits = await AnalyticUnit.findMany({ panelId });
const analyticUnitIds = analyticUnits.map(analyticUnit => analyticUnit.id);
const analyticUnitTemplates = analyticUnits.map(analyticUnit => analyticUnit.toTemplate());
const [caches, detectionSpans, segments] = await Promise.all([
AnalyticUnitCache.findMany({ _id: { $in: analyticUnitIds } }),
DetectionSpan.findByAnalyticUnitIds(analyticUnitIds),
Segment.findByAnalyticUnitIds(analyticUnitIds)
]);
return {
analyticUnitTemplates,
caches,
detectionSpans,
segments
};
}

7
server/src/types.ts

@ -0,0 +1,7 @@
// We should remove Omit definition when we'll be updating TypeScript
// It was introduced in TS v3.5.1:
// https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/#the-omit-helper-type
/**
* Construct a type with the properties of T except for those in type K.
*/
export type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; }
Loading…
Cancel
Save