mirror of https://github.com/grafana/grafana
Explore metrics: Consolidate filters with the OTel experience (#98371)
* sort otel resources to top of adhoc filters * add bool in datatrail * add function to find non promoted otel resources * add additional super filter variable * hide filters var and keep otel filters var hidden * add nonpromoted resources to state, update otel or var filters when super filter is updated * update comments * update plan * Allow deployment environment values from both metrics and target_info * Remove usage of dep env var in getting resources * update code comments for clarity * Remove dep env variable, autoselect dep env in otelmetricvar and allow updating of vals in otelmetricvar * Fix bug that conflicts with local storage useOtelExperience check * expose metadata to show data source is loaded to prevent otel race conditions * remove filtering check on target_info in the itel join query * update plan with extra issues * refactor update and reset functions for otel experience * use non promoted resources as the standardization check * sort the resources in filters var if using otel experience * add test for sorting resources with otelmetricsvar * update tests for otel experience in datatrail.test * update tests for otel utils * update otel api tests * update trail store tests to remove dep env var * run prettier * remove unused imports * add tests, distinguish on start and when the initial otel check is done, update comments * Fix bug when adding multiple otel resources * fix when adding filter from breakdown * add migration for dep env var * update migration function and write tests * prettier * Update dep env migration to handle bookmarks * fix trailstore tests for reintroducing the dep env var * refactor default env function, we only need the value * remove redundant check * move otel functions to utils and update and add tests * prettier * cleanup * fix migration for fromStart * update tests for migration * use join and use push * fix flow with state * Fix flow in update OTel function * update tests for flow fixes * fix toggle OTel bugs * report when dep env has been migrated and delete dep env filters to not migrate it again * Clear out dep env after migration * run prettier * improve non promoted attribute function * remove unused functions * prettier * default otel experience to off * report when otel experience is used * report when otel is turned on and off * report otel filters changed * prettier * keep default otel off, respect the local storage, but if loading with otel vars from url or bookmark we can turn it on * Add new badge * fix metric scene breakdown add filter bug around non promoted labels on a metric that are different than non promoted labels for all metrics * prettier * make i18n-extract * prettier for translations * change button name to "Filter" * Update public/app/features/trails/Breakdown/AddToFiltersGraphAction.tsx Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com> * Update public/app/features/trails/DataTrail.tsx Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com> * Update public/app/features/trails/migrations/otelDeploymentEnvironment.ts Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com> * Update public/app/features/trails/DataTrail.tsx Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com> * Update public/app/features/trails/otel/api.ts Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com> * Update public/app/features/trails/otel/util.ts Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com> * Add more padding for pill where capital letter gets to close to the left border and looks off. * clear up comments --------- Co-authored-by: Nick Richmond <5732000+NWRichmond@users.noreply.github.com>pull/98923/head
parent
6883a4b294
commit
fd4718df33
@ -0,0 +1,129 @@ |
||||
import { AdHocVariableFilter, UrlQueryMap, UrlQueryValue } from '@grafana/data'; |
||||
import { AdHocFiltersVariable, CustomVariable, sceneGraph } from '@grafana/scenes'; |
||||
|
||||
import { DataTrail } from '../DataTrail'; |
||||
import { VAR_OTEL_AND_METRIC_FILTERS, VAR_OTEL_DEPLOYMENT_ENV } from '../shared'; |
||||
|
||||
import { migrateOtelDeploymentEnvironment, migrateAdHocFilters } from './otelDeploymentEnvironment'; |
||||
|
||||
describe('migrate old dep env var to otel and metrics var', () => { |
||||
describe('migrateOtelDeploymentEnvironment', () => { |
||||
let trail = {} as DataTrail; |
||||
beforeEach(() => { |
||||
trail = new DataTrail({ |
||||
useOtelExperience: true, |
||||
}); |
||||
}); |
||||
it('should not be called if var-otel_and_metric_filters is present with label', () => { |
||||
// this variable being present indicates it has already been migrated
|
||||
const urlParams: UrlQueryMap = { |
||||
'var-otel_and_metric_filters': ['key|=|value'], |
||||
}; |
||||
|
||||
migrateOtelDeploymentEnvironment(trail, urlParams); |
||||
|
||||
const otelMetricsVar = getOtelAndMetricsVar(trail); |
||||
|
||||
expect(otelMetricsVar.state.filters).toEqual([]); |
||||
}); |
||||
|
||||
it('should not be called if starting a new trail', () => { |
||||
// new trails do not need to be migrated
|
||||
trail.setState({ startButtonClicked: true }); |
||||
|
||||
const urlParams: UrlQueryMap = { |
||||
'var-otel_and_metric_filters': [''], |
||||
}; |
||||
|
||||
migrateOtelDeploymentEnvironment(trail, urlParams); |
||||
|
||||
const otelMetricsVar = getOtelAndMetricsVar(trail); |
||||
|
||||
expect(otelMetricsVar.state.filters).toEqual([]); |
||||
}); |
||||
|
||||
it('should not migrate if var-deployment_environment is not present', () => { |
||||
const urlParams: UrlQueryMap = {}; |
||||
|
||||
migrateOtelDeploymentEnvironment(trail, urlParams); |
||||
|
||||
const otelMetricsVar = getOtelAndMetricsVar(trail); |
||||
|
||||
expect(otelMetricsVar.state.filters).toEqual([]); |
||||
}); |
||||
|
||||
it('should migrate deployment environment and set filters correctly', () => { |
||||
const urlParams: UrlQueryMap = { |
||||
'var-deployment_environment': ['env1', 'env2'], |
||||
'var-otel_resources': ['otelResource|=|value'], |
||||
'var-filters': ['metricFilter|=|value'], |
||||
}; |
||||
|
||||
migrateOtelDeploymentEnvironment(trail, urlParams); |
||||
|
||||
const expectedFilters = [ |
||||
{ |
||||
key: 'deployment_environment', |
||||
operator: '=~', |
||||
value: 'env1|env2', |
||||
}, |
||||
{ |
||||
key: 'otelResource', |
||||
operator: '=', |
||||
value: 'value', |
||||
}, |
||||
{ |
||||
key: 'metricFilter', |
||||
operator: '=', |
||||
value: 'value', |
||||
}, |
||||
// Add expected otelFilters and metricFilters here
|
||||
]; |
||||
|
||||
const otelMetricsVar = getOtelAndMetricsVar(trail); |
||||
expect(otelMetricsVar.state.filters).toEqual(expectedFilters); |
||||
// should clear out the dep env var
|
||||
const depEnvVar = getDepEnvVar(trail); |
||||
expect(depEnvVar.state.value).toBe(''); |
||||
}); |
||||
}); |
||||
|
||||
describe('migrateAdHocFilters', () => { |
||||
it('should return empty array when urlFilter is not present', () => { |
||||
const urlFilter: UrlQueryValue = null; |
||||
const filters: AdHocVariableFilter[] = []; |
||||
migrateAdHocFilters(urlFilter, filters); |
||||
expect(filters).toEqual([]); |
||||
}); |
||||
|
||||
it('should return filters when urlFilter is present', () => { |
||||
const urlFilter: UrlQueryValue = ['someKey|=|someValue']; |
||||
const filters: AdHocVariableFilter[] = []; |
||||
migrateAdHocFilters(urlFilter, filters); |
||||
const expected = [ |
||||
{ |
||||
key: 'someKey', |
||||
operator: '=', |
||||
value: 'someValue', |
||||
}, |
||||
]; |
||||
expect(filters).toEqual(expected); |
||||
}); |
||||
}); |
||||
|
||||
function getOtelAndMetricsVar(trail: DataTrail) { |
||||
const variable = sceneGraph.lookupVariable(VAR_OTEL_AND_METRIC_FILTERS, trail); |
||||
if (variable instanceof AdHocFiltersVariable) { |
||||
return variable; |
||||
} |
||||
throw new Error('getOtelAndMetricsVar failed'); |
||||
} |
||||
|
||||
function getDepEnvVar(trail: DataTrail) { |
||||
const variable = sceneGraph.lookupVariable(VAR_OTEL_DEPLOYMENT_ENV, trail); |
||||
if (variable instanceof CustomVariable) { |
||||
return variable; |
||||
} |
||||
throw new Error('getDepVar failed'); |
||||
} |
||||
}); |
@ -0,0 +1,111 @@ |
||||
import { AdHocVariableFilter, UrlQueryValue, UrlQueryMap } from '@grafana/data'; |
||||
import { sceneGraph, AdHocFiltersVariable, CustomVariable } from '@grafana/scenes'; |
||||
|
||||
import { DataTrail } from '../DataTrail'; |
||||
import { reportExploreMetrics } from '../interactions'; |
||||
import { VAR_OTEL_AND_METRIC_FILTERS, VAR_OTEL_DEPLOYMENT_ENV } from '../shared'; |
||||
|
||||
/** |
||||
* Migration for the otel deployment environment variable. |
||||
* When the deployment environment is present in the url, "var-deployment_environment", |
||||
* it is migrated to the new variable "var-otel_and_metric_filters." |
||||
* |
||||
* We check if the otel resources vars are also present in the url, "var-otel_resources" |
||||
* and if the metric filters are present in the url, "var-filters". |
||||
* |
||||
* Once all the variables are migrated to "var-otel_and_metric_filters", the rest is handled in trail.updateOtelData. |
||||
* |
||||
* @param trail |
||||
* @returns |
||||
*/ |
||||
export function migrateOtelDeploymentEnvironment(trail: DataTrail, urlParams: UrlQueryMap) { |
||||
const deploymentEnv = urlParams['var-deployment_environment']; |
||||
// does not need to be migrated
|
||||
const otelMetricsVar = urlParams['var-otel_and_metric_filters']; |
||||
|
||||
// this check is if it has already been migrated
|
||||
if ( |
||||
otelMetricsVar && |
||||
Array.isArray(otelMetricsVar) && |
||||
otelMetricsVar.length > 0 && |
||||
(trail.state.startButtonClicked || otelMetricsVar[0] !== '') |
||||
) { |
||||
return; |
||||
} |
||||
// if there is no dep env, does not need to be migrated
|
||||
if (!deploymentEnv) { |
||||
return; |
||||
} |
||||
|
||||
let filters: AdHocVariableFilter[] = []; |
||||
// if there is a deployment environment, we must also migrate the otel resources to the new variable
|
||||
const otelResources = urlParams['var-otel_resources']; |
||||
const metricVarfilters = urlParams['var-filters']; |
||||
if ( |
||||
Array.isArray(deploymentEnv) && |
||||
deploymentEnv.length > 0 && |
||||
deploymentEnv[0] !== '' && |
||||
deploymentEnv.every((r) => r && typeof r === 'string') |
||||
) { |
||||
// all the values are strings because they are prometheus labels
|
||||
// so we can safely cast them to strings
|
||||
const stringDepEnv = deploymentEnv.map((r) => r.toString()); |
||||
const value = stringDepEnv.join('|'); |
||||
|
||||
filters.push({ |
||||
key: 'deployment_environment', |
||||
operator: deploymentEnv.length > 1 ? '=~' : '=', |
||||
value, |
||||
}); |
||||
} |
||||
|
||||
// mutate the filters and add to them if we need to
|
||||
migrateAdHocFilters(otelResources, filters); |
||||
migrateAdHocFilters(metricVarfilters, filters); |
||||
|
||||
const otelAndMetricsFiltersVariable = sceneGraph.lookupVariable(VAR_OTEL_AND_METRIC_FILTERS, trail); |
||||
const deploymentEnvironmentVariable = sceneGraph.lookupVariable(VAR_OTEL_DEPLOYMENT_ENV, trail); |
||||
|
||||
if ( |
||||
!( |
||||
otelAndMetricsFiltersVariable instanceof AdHocFiltersVariable && |
||||
deploymentEnvironmentVariable instanceof CustomVariable |
||||
) |
||||
) { |
||||
return; |
||||
} |
||||
|
||||
otelAndMetricsFiltersVariable.setState({ |
||||
filters, |
||||
}); |
||||
// clear the deployment environment to not migrate it again
|
||||
reportExploreMetrics('deployment_environment_migrated', {}); |
||||
deploymentEnvironmentVariable.setState({ |
||||
value: '', |
||||
}); |
||||
} |
||||
|
||||
export function migrateAdHocFilters(urlFilter: UrlQueryValue, filters: AdHocVariableFilter[]) { |
||||
if ( |
||||
!( |
||||
urlFilter && // is present
|
||||
Array.isArray(urlFilter) && // is an array
|
||||
urlFilter.length > 0 && // has values
|
||||
urlFilter[0] !== '' && // empty vars can contain ''
|
||||
urlFilter.every((r) => r && typeof r === 'string') // vars are of any type but ours are all strings
|
||||
) |
||||
) { |
||||
return filters; |
||||
} |
||||
|
||||
urlFilter.forEach((filter) => { |
||||
const parts = filter.toString().split('|'); |
||||
filters.push({ |
||||
key: parts[0].toString(), |
||||
operator: parts[1].toString(), |
||||
value: parts[2].toString(), |
||||
}); |
||||
}); |
||||
|
||||
return filters; |
||||
} |
Loading…
Reference in new issue