Skip to content

feat: adding topology custom metric model and service #999

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Color } from '@hypertrace/common';
import { createModelFactory } from '@hypertrace/dashboards/testing';
import { TopologyMetricCategoryModel } from './topology-metric-category.model';

describe('Topology Metric with category model', () => {
const modelFactory = createModelFactory();

test('provides category name correctly', () => {
const spectator = modelFactory(TopologyMetricCategoryModel, {
properties: {
name: 'test name',
minValue: 0,
maxValue: 10,
fillColor: Color.Blue2,
strokeColor: Color.Blue3,
focusColor: Color.Blue4
}
});

expect(spectator.model.getCategoryClassName()).toContain('test-name');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Color } from '@hypertrace/common';
import { BOOLEAN_PROPERTY, Model, ModelProperty, NUMBER_PROPERTY, STRING_PROPERTY } from '@hypertrace/hyperdash';
import { kebabCase, uniqueId } from 'lodash-es';

@Model({
type: 'topology-metric-category'
})
export class TopologyMetricCategoryModel implements TopologyMetricCategoryData {
@ModelProperty({
key: 'name',
required: true,
type: STRING_PROPERTY.type
})
public name!: string;

@ModelProperty({
key: 'minValue',
required: true,
type: NUMBER_PROPERTY.type
})
public minValue!: number;

@ModelProperty({
key: 'maxValue',
required: false,
type: NUMBER_PROPERTY.type
})
public maxValue?: number;

@ModelProperty({
key: 'fillColor',
required: true,
type: STRING_PROPERTY.type
})
public fillColor!: Color;

@ModelProperty({
key: 'strokeColor',
required: true,
type: STRING_PROPERTY.type
})
public strokeColor!: Color;

@ModelProperty({
key: 'focusColor',
required: true,
type: STRING_PROPERTY.type
})
public focusColor!: Color;

@ModelProperty({
key: 'highestPrecedence',
required: false,
type: BOOLEAN_PROPERTY.type
})
public highestPrecedence?: boolean = false;

private readonly id: string = uniqueId();

public getCategoryClassName(): string {
return `${kebabCase(this.name)}-${this.id}`;
}
}

export interface TopologyMetricCategoryData {
name: string;
minValue: number;
maxValue?: number;
fillColor: string;
strokeColor: string;
focusColor: string;
highestPrecedence?: boolean;
getCategoryClassName(): string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Color } from '@hypertrace/common';
import { createModelFactory } from '@hypertrace/dashboards/testing';
import { MetricAggregationType } from '@hypertrace/distributed-tracing';
import { MetricAggregationSpecificationModel } from '../../specifiers/metric-aggregation-specification.model';
import { TopologyMetricCategoryModel } from './topology-metric-category.model';
import { TopologyMetricWithCategoryModel } from './topology-metric-with-category.model';

describe('Topology Metric with category model', () => {
const modelFactory = createModelFactory();

const createCategoryModel = (
name: string,
minValue: number,
fillColor: Color,
strokeColor: Color,
focusColor: Color,
maxValue?: number
): TopologyMetricCategoryModel => {
const categoryModel = new TopologyMetricCategoryModel();
categoryModel.name = name;
categoryModel.minValue = minValue;
categoryModel.maxValue = maxValue;
categoryModel.fillColor = fillColor;
categoryModel.strokeColor = strokeColor;
categoryModel.focusColor = focusColor;

return categoryModel;
};
test('provides category name correctly', () => {
const specification = new MetricAggregationSpecificationModel();
specification.metric = 'metric-name';
specification.aggregation = MetricAggregationType.Average;
specification.modelOnInit();

const categories = [
createCategoryModel('first', 0, Color.Blue2, Color.Blue3, Color.Blue4, 10),
createCategoryModel('second', 10, Color.Red1, Color.Red3, Color.Red4, 50),
createCategoryModel('third', 50, Color.Blue2, Color.Blue3, Color.Blue4)
];

const spectator = modelFactory(TopologyMetricWithCategoryModel, {
properties: {
specification: specification,
categories: categories
}
});

expect(
spectator.model.extractAndGetDataCategoryForMetric({
[specification.resultAlias()]: { value: 50 }
})
).toEqual(categories[2]);

expect(
spectator.model.extractAndGetDataCategoryForMetric({
[specification.resultAlias()]: { value: 5 }
})
).toEqual(categories[0]);

expect(
spectator.model.extractAndGetDataCategoryForMetric({
[specification.resultAlias()]: { value: 22 }
})
).toEqual(categories[1]);

expect(
spectator.model.extractAndGetDataCategoryForMetric({
[specification.resultAlias()]: { value: -10 }
})
).toEqual(undefined);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Dictionary } from '@hypertrace/common';
import { MetricAggregation } from '@hypertrace/distributed-tracing';
import {
ARRAY_PROPERTY,
Model,
ModelModelPropertyTypeInstance,
ModelProperty,
ModelPropertyType
} from '@hypertrace/hyperdash';
import { MetricAggregationSpecificationModel } from '../../specifiers/metric-aggregation-specification.model';
import { MetricAggregationSpecification } from './../../../../../graphql/model/schema/specifications/metric-aggregation-specification';
import { TopologyMetricCategoryData, TopologyMetricCategoryModel } from './topology-metric-category.model';

@Model({
type: 'topology-metric-with-category'
})
export class TopologyMetricWithCategoryModel implements TopologyMetricWithCategoryData {
@ModelProperty({
key: 'specification',
required: true,
// tslint:disable-next-line: no-object-literal-type-assertion
type: {
key: ModelPropertyType.TYPE,
defaultModelClass: MetricAggregationSpecificationModel
} as ModelModelPropertyTypeInstance
})
public specification!: MetricAggregationSpecificationModel;

@ModelProperty({
key: 'categories',
required: false,
type: ARRAY_PROPERTY.type
})
public categories: TopologyMetricCategoryModel[] = [];

public extractAndGetDataCategoryForMetric(data: Dictionary<unknown>): TopologyMetricCategoryData | undefined {
const aggregation = this.extractDataForMetric(data);

return aggregation !== undefined ? this.getDataCategoryForMetric(aggregation.value) : undefined;
}

public extractDataForMetric(data: Dictionary<unknown>): MetricAggregation | undefined {
return data[this.specification.resultAlias()] as MetricAggregation | undefined;
}

private getDataCategoryForMetric(value: number): TopologyMetricCategoryData | undefined {
return this.categories.find(
category => value >= category.minValue && (category.maxValue !== undefined ? value < category.maxValue : true)
);
}
}

export interface TopologyMetricWithCategoryData {
specification: MetricAggregationSpecification;
categories: TopologyMetricCategoryData[];
extractDataForMetric(data: Dictionary<unknown>): MetricAggregation | undefined;
extractAndGetDataCategoryForMetric(data: Dictionary<unknown>): TopologyMetricCategoryData | undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
ARRAY_PROPERTY,
Model,
ModelModelPropertyTypeInstance,
ModelProperty,
ModelPropertyType
} from '@hypertrace/hyperdash';
import { TopologyMetricWithCategoryData, TopologyMetricWithCategoryModel } from './topology-metric-with-category.model';

@Model({
type: 'topology-metrics'
})
export class TopologyMetricsModel implements TopologyMetricsData {
@ModelProperty({
key: 'primary',
required: true,
// tslint:disable-next-line: no-object-literal-type-assertion
type: {
key: ModelPropertyType.TYPE,
defaultModelClass: TopologyMetricWithCategoryModel
} as ModelModelPropertyTypeInstance
})
public primary!: TopologyMetricWithCategoryModel;

@ModelProperty({
key: 'secondary',
required: false,
// tslint:disable-next-line: no-object-literal-type-assertion
type: {
key: ModelPropertyType.TYPE,
defaultModelClass: TopologyMetricWithCategoryModel
} as ModelModelPropertyTypeInstance
})
public secondary?: TopologyMetricWithCategoryModel;

@ModelProperty({
key: 'others',
required: false,
type: ARRAY_PROPERTY.type
})
public others?: TopologyMetricWithCategoryModel[];
}

export interface TopologyMetricsData {
primary: TopologyMetricWithCategoryData;
secondary?: TopologyMetricWithCategoryData;
others?: TopologyMetricWithCategoryData[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Color } from '@hypertrace/common';
import { TopologyMetricCategoryData } from '../../../data/graphql/topology/metrics/topology-metric-category.model';

const enum PrimaryEdgeMetricCategoryValueType {
LessThan20 = 'less-than-20',
From20To100 = 'from-20-to-100',
From100To500 = 'from-100-to-500',
From500To1000 = 'from-500-to-1000',
GreaterThanOrEqualTo1000 = 'greater-than-or-equal-to-1000',
NotSpecified = 'not-specified'
}

const enum SecondaryEdgeMetricCategoryValueType {
LessThan5 = 'less-than-5',
GreaterThanOrEqualTo5 = 'greater-than-or-equal-to-5',
NotSpecified = 'not-specified'
}

export const defaultPrimaryEdgeMetricCategories: Omit<TopologyMetricCategoryData, 'getCategoryClassName'>[] = [
{
name: PrimaryEdgeMetricCategoryValueType.LessThan20,
minValue: 0,
maxValue: 20,
fillColor: Color.BlueGray1,
strokeColor: Color.BlueGray1,
focusColor: Color.BlueGray1
},
{
name: PrimaryEdgeMetricCategoryValueType.From20To100,
minValue: 20,
maxValue: 100,
fillColor: Color.BlueGray2,
strokeColor: Color.BlueGray2,
focusColor: Color.BlueGray2
},
{
name: PrimaryEdgeMetricCategoryValueType.From100To500,
minValue: 100,
maxValue: 500,
fillColor: Color.BlueGray3,
strokeColor: Color.BlueGray3,
focusColor: Color.BlueGray3
},
{
name: PrimaryEdgeMetricCategoryValueType.From500To1000,
minValue: 500,
maxValue: 1000,
fillColor: Color.BlueGray4,
strokeColor: Color.BlueGray4,
focusColor: Color.BlueGray4
},
{
name: PrimaryEdgeMetricCategoryValueType.GreaterThanOrEqualTo1000,
minValue: 1000,
maxValue: undefined,
fillColor: Color.BlueGray4,
strokeColor: Color.BlueGray4,
focusColor: Color.BlueGray4
}
];

export const defaultSecondaryEdgeMetricCategories: Omit<TopologyMetricCategoryData, 'getCategoryClassName'>[] = [
{
name: SecondaryEdgeMetricCategoryValueType.LessThan5,
minValue: 0,
maxValue: 5,
fillColor: Color.Gray2,
strokeColor: Color.Gray2,
focusColor: Color.Gray2
},
{
name: SecondaryEdgeMetricCategoryValueType.GreaterThanOrEqualTo5,
minValue: 5,
maxValue: undefined,
fillColor: Color.Red5,
strokeColor: Color.Red5,
focusColor: Color.Red5,
highestPrecedence: true
}
];
Loading