Commit 202a150f authored by Jose Ivan Vargas's avatar Jose Ivan Vargas Committed by Fatih Acet

Add support for additional panel types

This introduces the single stat chart component aside from
the area chart component
parent ef348618
...@@ -8,6 +8,7 @@ import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; ...@@ -8,6 +8,7 @@ import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import { chartHeight, graphTypes, lineTypes } from '../../constants'; import { chartHeight, graphTypes, lineTypes } from '../../constants';
import { makeDataSeries } from '~/helpers/monitor_helper'; import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils';
let debouncedResize; let debouncedResize;
...@@ -23,19 +24,7 @@ export default { ...@@ -23,19 +24,7 @@ export default {
graphData: { graphData: {
type: Object, type: Object,
required: true, required: true,
validator(data) { validator: graphDataValidatorForValues.bind(null, false),
return (
Array.isArray(data.queries) &&
data.queries.filter(query => {
if (Array.isArray(query.result)) {
return (
query.result.filter(res => Array.isArray(res.values)).length === query.result.length
);
}
return false;
}).length === data.queries.length
);
},
}, },
containerWidth: { containerWidth: {
type: Number, type: Number,
......
...@@ -4,6 +4,7 @@ import { debounceByAnimationFrame } from '~/lib/utils/common_utils'; ...@@ -4,6 +4,7 @@ import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils'; import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import { chartHeight } from '../../constants'; import { chartHeight } from '../../constants';
import { makeDataSeries } from '~/helpers/monitor_helper'; import { makeDataSeries } from '~/helpers/monitor_helper';
import { graphDataValidatorForValues } from '../../utils';
export default { export default {
components: { components: {
...@@ -14,25 +15,13 @@ export default { ...@@ -14,25 +15,13 @@ export default {
graphData: { graphData: {
type: Object, type: Object,
required: true, required: true,
validator(data) { validator: graphDataValidatorForValues.bind(null, false),
return (
Array.isArray(data.queries) &&
data.queries.filter(query => {
if (Array.isArray(query.result)) {
return (
query.result.filter(res => Array.isArray(res.values)).length === query.result.length
);
}
return false;
}).length === data.queries.length
);
}, },
containerWidth: { containerWidth: {
type: Number, type: Number,
required: true, required: true,
}, },
}, },
},
data() { data() {
return { return {
width: 0, width: 0,
......
<script> <script>
import { GlSingleStat } from '@gitlab/ui/dist/charts'; import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { roundOffFloat } from '~/lib/utils/common_utils';
import { graphDataValidatorForValues } from '../../utils';
export default { export default {
components: { components: {
...@@ -7,22 +9,21 @@ export default { ...@@ -7,22 +9,21 @@ export default {
}, },
inheritAttrs: false, inheritAttrs: false,
props: { props: {
title: { graphData: {
type: String, type: Object,
required: true, required: true,
validator: graphDataValidatorForValues.bind(null, true),
}, },
value: {
type: Number,
required: true,
}, },
unit: { computed: {
type: String, queryInfo() {
required: true, return this.graphData.queries[0];
}, },
engineeringNotation() {
return `${roundOffFloat(this.queryInfo.result[0].value[1], 1)}${this.queryInfo.unit}`;
}, },
computed: { graphTitle() {
valueWithUnit() { return this.queryInfo.label;
return `${this.value}${this.unit}`;
}, },
}, },
}; };
...@@ -30,8 +31,8 @@ export default { ...@@ -30,8 +31,8 @@ export default {
<template> <template>
<div class="prometheus-graph col-12 col-lg-6"> <div class="prometheus-graph col-12 col-lg-6">
<div class="prometheus-graph-header"> <div class="prometheus-graph-header">
<h5 ref="graphTitle" class="prometheus-graph-title">{{ title }}</h5> <h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5>
</div> </div>
<gl-single-stat :value="valueWithUnit" :title="title" variant="success" /> <gl-single-stat :value="engineeringNotation" :title="graphTitle" variant="success" />
</div> </div>
</template> </template>
...@@ -7,6 +7,8 @@ import Icon from '~/vue_shared/components/icon.vue'; ...@@ -7,6 +7,8 @@ import Icon from '~/vue_shared/components/icon.vue';
import { getParameterValues } from '~/lib/utils/url_utility'; import { getParameterValues } from '~/lib/utils/url_utility';
import invalidUrl from '~/lib/utils/invalid_url'; import invalidUrl from '~/lib/utils/invalid_url';
import MonitorAreaChart from './charts/area.vue'; import MonitorAreaChart from './charts/area.vue';
import MonitorSingleStatChart from './charts/single_stat.vue';
import PanelType from './panel_type.vue';
import GraphGroup from './graph_group.vue'; import GraphGroup from './graph_group.vue';
import EmptyState from './empty_state.vue'; import EmptyState from './empty_state.vue';
import { timeWindows, timeWindowsKeyNames } from '../constants'; import { timeWindows, timeWindowsKeyNames } from '../constants';
...@@ -18,6 +20,8 @@ let sidebarMutationObserver; ...@@ -18,6 +20,8 @@ let sidebarMutationObserver;
export default { export default {
components: { components: {
MonitorAreaChart, MonitorAreaChart,
MonitorSingleStatChart,
PanelType,
GraphGroup, GraphGroup,
EmptyState, EmptyState,
Icon, Icon,
...@@ -152,6 +156,7 @@ export default { ...@@ -152,6 +156,7 @@ export default {
'useDashboardEndpoint', 'useDashboardEndpoint',
'allDashboards', 'allDashboards',
'multipleDashboardsEnabled', 'multipleDashboardsEnabled',
'additionalPanelTypesEnabled',
]), ]),
groupsWithData() { groupsWithData() {
return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0); return this.groups.filter(group => this.chartsWithData(group.metrics).length > 0);
...@@ -173,6 +178,7 @@ export default { ...@@ -173,6 +178,7 @@ export default {
deploymentsEndpoint: this.deploymentsEndpoint, deploymentsEndpoint: this.deploymentsEndpoint,
dashboardEndpoint: this.dashboardEndpoint, dashboardEndpoint: this.dashboardEndpoint,
currentDashboard: this.currentDashboard, currentDashboard: this.currentDashboard,
projectPath: this.projectPath,
}); });
this.timeWindows = timeWindows; this.timeWindows = timeWindows;
...@@ -220,6 +226,8 @@ export default { ...@@ -220,6 +226,8 @@ export default {
chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)), chart.metrics.some(metric => this.metricsWithData.includes(metric.metric_id)),
); );
}, },
// TODO: BEGIN, Duplicated code with panel_type until feature flag is removed
// Issue number: https://gitlab.com/gitlab-org/gitlab-ce/issues/63845
getGraphAlerts(queries) { getGraphAlerts(queries) {
if (!this.allAlerts) return {}; if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId); const metricIdsForChart = queries.map(q => q.metricId);
...@@ -228,6 +236,7 @@ export default { ...@@ -228,6 +236,7 @@ export default {
getGraphAlertValues(queries) { getGraphAlertValues(queries) {
return Object.values(this.getGraphAlerts(queries)); return Object.values(this.getGraphAlerts(queries));
}, },
// TODO: END
hideAddMetricModal() { hideAddMetricModal() {
this.$refs.addMetricModal.hide(); this.$refs.addMetricModal.hide();
}, },
...@@ -366,14 +375,23 @@ export default { ...@@ -366,14 +375,23 @@ export default {
:name="groupData.group" :name="groupData.group"
:show-panels="showPanels" :show-panels="showPanels"
> >
<template v-if="additionalPanelTypesEnabled">
<panel-type
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:key="`panel-type-${graphIndex}`"
:graph-data="graphData"
:dashboard-width="elWidth"
/>
</template>
<template v-else>
<monitor-area-chart <monitor-area-chart
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)" v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:key="graphIndex" :key="graphIndex"
:project-path="projectPath"
:graph-data="graphData" :graph-data="graphData"
:deployment-data="deploymentData" :deployment-data="deploymentData"
:thresholds="getGraphAlertValues(graphData.queries)" :thresholds="getGraphAlertValues(graphData.queries)"
:container-width="elWidth" :container-width="elWidth"
:project-path="projectPath"
group-id="monitor-area-chart" group-id="monitor-area-chart"
> >
<alert-widget <alert-widget
...@@ -384,6 +402,7 @@ export default { ...@@ -384,6 +402,7 @@ export default {
@setAlerts="setAlerts" @setAlerts="setAlerts"
/> />
</monitor-area-chart> </monitor-area-chart>
</template>
</graph-group> </graph-group>
</div> </div>
<empty-state <empty-state
......
<script>
import { mapState } from 'vuex';
import _ from 'underscore';
import MonitorAreaChart from './charts/area.vue';
import MonitorSingleStatChart from './charts/single_stat.vue';
export default {
components: {
MonitorAreaChart,
MonitorSingleStatChart,
},
props: {
graphData: {
type: Object,
required: true,
},
dashboardWidth: {
type: Number,
required: true,
},
},
computed: {
...mapState('monitoringDashboard', ['deploymentData', 'projectPath']),
alertWidgetAvailable() {
return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData;
},
},
methods: {
getGraphAlerts(queries) {
if (!this.allAlerts) return {};
const metricIdsForChart = queries.map(q => q.metricId);
return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId));
},
getGraphAlertValues(queries) {
return Object.values(this.getGraphAlerts(queries));
},
isPanelType(type) {
return this.graphData.type && this.graphData.type === type;
},
},
};
</script>
<template>
<monitor-single-stat-chart v-if="isPanelType('single-stat')" :graph-data="graphData" />
<monitor-area-chart
v-else
:graph-data="graphData"
:deployment-data="deploymentData"
:project-path="projectPath"
:thresholds="getGraphAlertValues(graphData.queries)"
:container-width="dashboardWidth"
group-id="monitor-area-chart"
>
<alert-widget
v-if="alertWidgetAvailable"
:alerts-endpoint="alertsEndpoint"
:relevant-queries="graphData.queries"
:alerts-to-manage="getGraphAlerts(graphData.queries)"
@setAlerts="setAlerts"
/>
</monitor-area-chart>
</template>
...@@ -12,6 +12,7 @@ export default (props = {}) => { ...@@ -12,6 +12,7 @@ export default (props = {}) => {
store.dispatch('monitoringDashboard/setFeatureFlags', { store.dispatch('monitoringDashboard/setFeatureFlags', {
prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint, prometheusEndpointEnabled: gon.features.environmentMetricsUsePrometheusEndpoint,
multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards, multipleDashboardsEnabled: gon.features.environmentMetricsShowMultipleDashboards,
additionalPanelTypesEnabled: gon.features.environmentMetricsAdditionalPanelTypes,
}); });
} }
......
...@@ -37,10 +37,11 @@ export const setEndpoints = ({ commit }, endpoints) => { ...@@ -37,10 +37,11 @@ export const setEndpoints = ({ commit }, endpoints) => {
export const setFeatureFlags = ( export const setFeatureFlags = (
{ commit }, { commit },
{ prometheusEndpointEnabled, multipleDashboardsEnabled }, { prometheusEndpointEnabled, multipleDashboardsEnabled, additionalPanelTypesEnabled },
) => { ) => {
commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled); commit(types.SET_DASHBOARD_ENABLED, prometheusEndpointEnabled);
commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled); commit(types.SET_MULTIPLE_DASHBOARDS_ENABLED, multipleDashboardsEnabled);
commit(types.SET_ADDITIONAL_PANEL_TYPES_ENABLED, additionalPanelTypesEnabled);
}; };
export const requestMetricsDashboard = ({ commit }) => { export const requestMetricsDashboard = ({ commit }) => {
......
...@@ -11,6 +11,7 @@ export const SET_QUERY_RESULT = 'SET_QUERY_RESULT'; ...@@ -11,6 +11,7 @@ export const SET_QUERY_RESULT = 'SET_QUERY_RESULT';
export const SET_TIME_WINDOW = 'SET_TIME_WINDOW'; export const SET_TIME_WINDOW = 'SET_TIME_WINDOW';
export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED'; export const SET_DASHBOARD_ENABLED = 'SET_DASHBOARD_ENABLED';
export const SET_MULTIPLE_DASHBOARDS_ENABLED = 'SET_MULTIPLE_DASHBOARDS_ENABLED'; export const SET_MULTIPLE_DASHBOARDS_ENABLED = 'SET_MULTIPLE_DASHBOARDS_ENABLED';
export const SET_ADDITIONAL_PANEL_TYPES_ENABLED = 'SET_ADDITIONAL_PANEL_TYPES_ENABLED';
export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS'; export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS';
export const SET_ENDPOINTS = 'SET_ENDPOINTS'; export const SET_ENDPOINTS = 'SET_ENDPOINTS';
export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE'; export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE';
......
...@@ -75,6 +75,7 @@ export default { ...@@ -75,6 +75,7 @@ export default {
state.deploymentsEndpoint = endpoints.deploymentsEndpoint; state.deploymentsEndpoint = endpoints.deploymentsEndpoint;
state.dashboardEndpoint = endpoints.dashboardEndpoint; state.dashboardEndpoint = endpoints.dashboardEndpoint;
state.currentDashboard = endpoints.currentDashboard; state.currentDashboard = endpoints.currentDashboard;
state.projectPath = endpoints.projectPath;
}, },
[types.SET_DASHBOARD_ENABLED](state, enabled) { [types.SET_DASHBOARD_ENABLED](state, enabled) {
state.useDashboardEndpoint = enabled; state.useDashboardEndpoint = enabled;
...@@ -92,4 +93,7 @@ export default { ...@@ -92,4 +93,7 @@ export default {
[types.SET_ALL_DASHBOARDS](state, dashboards) { [types.SET_ALL_DASHBOARDS](state, dashboards) {
state.allDashboards = dashboards; state.allDashboards = dashboards;
}, },
[types.SET_ADDITIONAL_PANEL_TYPES_ENABLED](state, enabled) {
state.additionalPanelTypesEnabled = enabled;
},
}; };
...@@ -9,6 +9,7 @@ export default () => ({ ...@@ -9,6 +9,7 @@ export default () => ({
dashboardEndpoint: invalidUrl, dashboardEndpoint: invalidUrl,
useDashboardEndpoint: false, useDashboardEndpoint: false,
multipleDashboardsEnabled: false, multipleDashboardsEnabled: false,
additionalPanelTypesEnabled: false,
emptyState: 'gettingStarted', emptyState: 'gettingStarted',
showEmptyState: true, showEmptyState: true,
groups: [], groups: [],
...@@ -17,4 +18,5 @@ export default () => ({ ...@@ -17,4 +18,5 @@ export default () => ({
metricsWithData: [], metricsWithData: [],
allDashboards: [], allDashboards: [],
currentDashboard: null, currentDashboard: null,
projectPath: null,
}); });
...@@ -69,13 +69,26 @@ export const sortMetrics = metrics => ...@@ -69,13 +69,26 @@ export const sortMetrics = metrics =>
.sortBy('weight') .sortBy('weight')
.value(); .value();
export const normalizeQueryResult = timeSeries => ({ export const normalizeQueryResult = timeSeries => {
let normalizedResult = {};
if (timeSeries.values) {
normalizedResult = {
...timeSeries, ...timeSeries,
values: timeSeries.values.map(([timestamp, value]) => [ values: timeSeries.values.map(([timestamp, value]) => [
new Date(timestamp * 1000).toISOString(), new Date(timestamp * 1000).toISOString(),
Number(value), Number(value),
]), ]),
}); };
} else if (timeSeries.value) {
normalizedResult = {
...timeSeries,
value: [new Date(timeSeries.value[0] * 1000).toISOString(), Number(timeSeries.value[1])],
};
}
return normalizedResult;
};
export const normalizeMetrics = metrics => { export const normalizeMetrics = metrics => {
const groupedMetrics = groupQueriesByChartInfo(metrics); const groupedMetrics = groupQueriesByChartInfo(metrics);
......
...@@ -30,4 +30,28 @@ export const getTimeDiff = selectedTimeWindow => { ...@@ -30,4 +30,28 @@ export const getTimeDiff = selectedTimeWindow => {
return { start, end }; return { start, end };
}; };
/**
* This method is used to validate if the graph data format for a chart component
* that needs a time series as a response from a prometheus query (query_range) is
* of a valid format or not.
* @param {Object} graphData the graph data response from a prometheus request
* @returns {boolean} whether the graphData format is correct
*/
export const graphDataValidatorForValues = (isValues, graphData) => {
const responseValueKeyName = isValues ? 'value' : 'values';
return (
Array.isArray(graphData.queries) &&
graphData.queries.filter(query => {
if (Array.isArray(query.result)) {
return (
query.result.filter(res => Array.isArray(res[responseValueKeyName])).length ===
query.result.length
);
}
return false;
}).length === graphData.queries.length
);
};
export default {}; export default {};
...@@ -13,6 +13,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -13,6 +13,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do before_action only: [:metrics, :additional_metrics, :metrics_dashboard] do
push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint) push_frontend_feature_flag(:environment_metrics_use_prometheus_endpoint)
push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards) push_frontend_feature_flag(:environment_metrics_show_multiple_dashboards)
push_frontend_feature_flag(:environment_metrics_additional_panel_types)
push_frontend_feature_flag(:prometheus_computed_alerts) push_frontend_feature_flag(:prometheus_computed_alerts)
end end
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import SingleStatChart from '~/monitoring/components/charts/single_stat.vue'; import SingleStatChart from '~/monitoring/components/charts/single_stat.vue';
import { graphDataPrometheusQuery } from '../mock_data';
describe('Single Stat Chart component', () => { describe('Single Stat Chart component', () => {
let singleStatChart; let singleStatChart;
...@@ -7,9 +8,7 @@ describe('Single Stat Chart component', () => { ...@@ -7,9 +8,7 @@ describe('Single Stat Chart component', () => {
beforeEach(() => { beforeEach(() => {
singleStatChart = shallowMount(SingleStatChart, { singleStatChart = shallowMount(SingleStatChart, {
propsData: { propsData: {
title: 'Time to render', graphData: graphDataPrometheusQuery,
value: 1,
unit: 'sec',
}, },
}); });
}); });
...@@ -19,9 +18,9 @@ describe('Single Stat Chart component', () => { ...@@ -19,9 +18,9 @@ describe('Single Stat Chart component', () => {
}); });
describe('computed', () => { describe('computed', () => {
describe('valueWithUnit', () => { describe('engineeringNotation', () => {
it('should interpolate the value and unit props', () => { it('should interpolate the value and unit props', () => {
expect(singleStatChart.vm.valueWithUnit).toBe('1sec'); expect(singleStatChart.vm.engineeringNotation).toBe('91MB');
}); });
}); });
}); });
......
...@@ -935,3 +935,75 @@ export const dashboardGitResponse = [ ...@@ -935,3 +935,75 @@ export const dashboardGitResponse = [
default: false, default: false,
}, },
]; ];
export const graphDataPrometheusQuery = {
title: 'Super Chart A2',
type: 'single-stat',
weight: 2,
metrics: [
{
id: 'metric_a1',
metric_id: 2,
query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
},
],
queries: [
{
metricId: null,
id: 'metric_a1',
metric_id: 2,
query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
result: [
{
metric: { job: 'prometheus' },
value: ['2019-06-26T21:03:20.881Z', 91],
},
],
},
],
};
export const graphDataPrometheusQueryRange = {
title: 'Super Chart A1',
type: 'area',
weight: 2,
metrics: [
{
id: 'metric_a1',
metric_id: 2,
query_range:
'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
},
],
queries: [
{
metricId: null,
id: 'metric_a1',
metric_id: 2,
query_range:
'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
unit: 'MB',
label: 'Total Consumption',
prometheus_endpoint_path:
'/root/kubernetes-gke-project/environments/35/prometheus/api/v1/query?query=max%28go_memstats_alloc_bytes%7Bjob%3D%22prometheus%22%7D%29+by+%28job%29+%2F1024%2F1024',
result: [
{
metric: {},
values: [[1495700554.925, '8.0390625'], [1495700614.925, '8.0390625']],
},
],
},
],
};
...@@ -115,12 +115,14 @@ describe('Monitoring mutations', () => { ...@@ -115,12 +115,14 @@ describe('Monitoring mutations', () => {
environmentsEndpoint: 'environments.json', environmentsEndpoint: 'environments.json',
deploymentsEndpoint: 'deployments.json', deploymentsEndpoint: 'deployments.json',
dashboardEndpoint: 'dashboard.json', dashboardEndpoint: 'dashboard.json',
projectPath: '/gitlab-org/gitlab-ce',
}); });
expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json'); expect(stateCopy.metricsEndpoint).toEqual('additional_metrics.json');
expect(stateCopy.environmentsEndpoint).toEqual('environments.json'); expect(stateCopy.environmentsEndpoint).toEqual('environments.json');
expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json'); expect(stateCopy.deploymentsEndpoint).toEqual('deployments.json');
expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json'); expect(stateCopy.dashboardEndpoint).toEqual('dashboard.json');
expect(stateCopy.projectPath).toEqual('/gitlab-org/gitlab-ce');
}); });
}); });
......
import { getTimeDiff } from '~/monitoring/utils'; import { getTimeDiff, graphDataValidatorForValues } from '~/monitoring/utils';
import { timeWindows } from '~/monitoring/constants'; import { timeWindows } from '~/monitoring/constants';
import { graphDataPrometheusQuery, graphDataPrometheusQueryRange } from './mock_data';
describe('getTimeDiff', () => { describe('getTimeDiff', () => {
it('defaults to an 8 hour (28800s) difference', () => { it('defaults to an 8 hour (28800s) difference', () => {
...@@ -27,3 +28,27 @@ describe('getTimeDiff', () => { ...@@ -27,3 +28,27 @@ describe('getTimeDiff', () => {
}); });
}); });
}); });
describe('graphDataValidatorForValues', () => {
/*
* When dealing with a metric using the query format, e.g.
* query: 'max(go_memstats_alloc_bytes{job="prometheus"}) by (job) /1024/1024'
* the validator will look for the `value` key instead of `values`
*/
it('validates data with the query format', () => {
const validGraphData = graphDataValidatorForValues(true, graphDataPrometheusQuery);
expect(validGraphData).toBe(true);
});
/*
* When dealing with a metric using the query?range format, e.g.
* query_range: 'avg(sum(container_memory_usage_bytes{container_name!="POD",pod_name=~"^%{ci_environment_slug}-(.*)",namespace="%{kube_namespace}"}) by (job)) without (job) /1024/1024/1024',
* the validator will look for the `values` key instead of `value`
*/
it('validates data with the query_range format', () => {
const validGraphData = graphDataValidatorForValues(false, graphDataPrometheusQueryRange);
expect(validGraphData).toBe(true);
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment