mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 04:31:36 +08:00

* Promisify loading schema - Move schema loading to LogsQueryEditor - Improve typing - Switch callbacks to promises * Update types * Refactor backend for new props - Rename intersectTime - Support setting timeColumn - Add additional properties to logs request body * Update applyTemplateVariables * Update set functions * Add new TimeManagement component * Update LogsQueryEditor * Hardcode timestamp column for traces queries * Ensure timeColumn is always set for log queries * Update tests * Update frontend tests * Readd type to make migration easier * Add migration * Add fake schema * Use predefined type * Update checks and defaults * Add tests * README updates * README update * Type update * Lint * More linting and type fixing * Error silently * More linting and typing * Update betterer * Update test * Simplify default column setting * Fix default column setting * Add tracking * Review - Fix typo on comment - Destructure and remove type assertion - Break out await into two variables - Remove lets and rename variable for clarity
285 lines
11 KiB
TypeScript
285 lines
11 KiB
TypeScript
import { AzureMetricDimension, AzureMonitorQuery, AzureQueryType, ResultFormat } from '../types';
|
||
|
||
import migrateQuery from './migrateQuery';
|
||
|
||
const azureMonitorQueryV8 = {
|
||
azureMonitor: {
|
||
aggregation: 'Average',
|
||
dimensionFilters: [],
|
||
metricName: 'dependencies/duration',
|
||
metricNamespace: 'microsoft.insights/components',
|
||
resourceGroup: 'cloud-datasources',
|
||
resourceName: 'AppInsightsTestData',
|
||
timeGrain: 'auto',
|
||
},
|
||
azureLogAnalytics: {
|
||
query:
|
||
'//change this example to create your own time series query\n<table name> //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by <group by column>, bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc',
|
||
resultFormat: ResultFormat.TimeSeries,
|
||
},
|
||
datasource: {
|
||
type: 'grafana-azure-monitor-datasource',
|
||
uid: 'sD-ZuB87k',
|
||
},
|
||
queryType: AzureQueryType.AzureMonitor,
|
||
refId: 'A',
|
||
subscription: '44693801-6ee6-49de-9b2d-9106972f9572',
|
||
};
|
||
|
||
const azureMonitorQueryV9_0 = {
|
||
azureMonitor: {
|
||
aggregation: 'Average',
|
||
dimensionFilters: [],
|
||
metricName: 'dependencies/duration',
|
||
metricNamespace: 'microsoft.insights/components',
|
||
resourceGroup: 'cloud-datasources',
|
||
resourceName: 'AppInsightsTestData',
|
||
resourceUri:
|
||
'/subscriptions/44693801-6ee6-49de-9b2d-9106972f9572/resourceGroups/cloud-datasources/providers/microsoft.insights/components/AppInsightsTestData',
|
||
timeGrain: 'auto',
|
||
},
|
||
azureLogAnalytics: {
|
||
query:
|
||
'//change this example to create your own time series query\n<table name> //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by <group by column>, bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc',
|
||
resultFormat: ResultFormat.TimeSeries,
|
||
},
|
||
datasource: {
|
||
type: 'grafana-azure-monitor-datasource',
|
||
uid: 'sD-ZuB87k',
|
||
},
|
||
queryType: AzureQueryType.AzureMonitor,
|
||
refId: 'A',
|
||
};
|
||
|
||
const modernMetricsQuery: AzureMonitorQuery = {
|
||
azureLogAnalytics: {
|
||
query:
|
||
'//change this example to create your own time series query\n<table name> //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full chart’s time range, choose the datetime column here\n| summarize count() by <group by column>, bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc',
|
||
resultFormat: ResultFormat.TimeSeries,
|
||
workspace: 'mock-workspace-id',
|
||
dashboardTime: false,
|
||
},
|
||
azureMonitor: {
|
||
aggregation: 'Average',
|
||
alias: '{{ dimensionvalue }}',
|
||
allowedTimeGrainsMs: [60000, 300000, 900000, 1800000, 3600000, 21600000, 43200000, 86400000],
|
||
dimensionFilters: [{ dimension: 'dependency/success', filters: ['*'], operator: 'eq' }],
|
||
metricName: 'dependencies/duration',
|
||
metricNamespace: 'microsoft.insights/components',
|
||
resources: [
|
||
{
|
||
resourceGroup: 'cloud-datasources',
|
||
resourceName: 'AppInsightsTestData',
|
||
},
|
||
],
|
||
timeGrain: 'PT5M',
|
||
top: '10',
|
||
},
|
||
azureResourceGraph: { resultFormat: 'table' },
|
||
queryType: AzureQueryType.AzureMonitor,
|
||
refId: 'A',
|
||
subscription: '44693801-6ee6-49de-9b2d-9106972f9572',
|
||
subscriptions: ['44693801-6ee6-49de-9b2d-9106972f9572'],
|
||
};
|
||
|
||
describe('AzureMonitor: migrateQuery', () => {
|
||
it('modern queries should not change', () => {
|
||
const result = migrateQuery(modernMetricsQuery);
|
||
|
||
// MUST use .toBe because we want to assert that the identity of unmigrated queries remains the same
|
||
expect(modernMetricsQuery).toBe(result);
|
||
});
|
||
|
||
describe('migrating from a v8 query to the latest query version', () => {
|
||
it('will not change valid dimension filters', () => {
|
||
const dimensionFilters: AzureMetricDimension[] = [
|
||
{ dimension: 'TestDimension', operator: 'eq', filters: ['testFilter'] },
|
||
];
|
||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
dimensionFilters,
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
it('correctly updates old filter containing wildcard', () => {
|
||
const dimensionFilters: AzureMetricDimension[] = [{ dimension: 'TestDimension', operator: 'eq', filter: '*' }];
|
||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
dimensionFilters: [
|
||
{ dimension: dimensionFilters[0].dimension, operator: dimensionFilters[0].operator, filters: ['*'] },
|
||
],
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
it('correctly updates old filter containing value', () => {
|
||
const dimensionFilters: AzureMetricDimension[] = [{ dimension: 'TestDimension', operator: 'eq', filter: 'test' }];
|
||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
dimensionFilters: [
|
||
{ dimension: dimensionFilters[0].dimension, operator: dimensionFilters[0].operator, filters: ['test'] },
|
||
],
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
it('correctly ignores wildcard if filters has a value', () => {
|
||
const dimensionFilters: AzureMetricDimension[] = [
|
||
{ dimension: 'TestDimension', operator: 'eq', filter: '*', filters: ['testFilter'] },
|
||
];
|
||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
dimensionFilters: [
|
||
{
|
||
dimension: dimensionFilters[0].dimension,
|
||
operator: dimensionFilters[0].operator,
|
||
filters: ['testFilter'],
|
||
},
|
||
],
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
it('correctly ignores duplicates', () => {
|
||
const dimensionFilters: AzureMetricDimension[] = [
|
||
{ dimension: 'TestDimension', operator: 'eq', filter: 'testFilter', filters: ['testFilter'] },
|
||
];
|
||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
dimensionFilters: [
|
||
{
|
||
dimension: dimensionFilters[0].dimension,
|
||
operator: dimensionFilters[0].operator,
|
||
filters: ['testFilter'],
|
||
},
|
||
],
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
it('correctly removes outdated fields', () => {
|
||
const result = migrateQuery({
|
||
...azureMonitorQueryV8,
|
||
azureMonitor: { dimension: 'testDimension', dimensionFilter: 'testFilter' },
|
||
});
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
dimensionFilters: [
|
||
{
|
||
dimension: 'testDimension',
|
||
operator: 'eq',
|
||
filters: ['testFilter'],
|
||
},
|
||
],
|
||
}),
|
||
})
|
||
);
|
||
expect(result.azureMonitor).not.toHaveProperty('dimension');
|
||
expect(result.azureMonitor).not.toHaveProperty('dimensionFilter');
|
||
});
|
||
|
||
it('correctly migrates a metric definition', () => {
|
||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { metricDefinition: 'ms.ns/mn' } });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
metricNamespace: 'ms.ns/mn',
|
||
metricDefinition: undefined,
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
|
||
it('correctly adds the dashboardTime property', () => {
|
||
const result = migrateQuery({ ...azureMonitorQueryV8 });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureLogAnalytics: expect.objectContaining({
|
||
dashboardTime: false,
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
});
|
||
|
||
describe('migrating from a v9.0 query to the latest query version', () => {
|
||
it('will parse the resource URI', () => {
|
||
const result = migrateQuery(azureMonitorQueryV9_0);
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
subscription: modernMetricsQuery.subscription,
|
||
azureMonitor: expect.objectContaining({
|
||
metricNamespace: modernMetricsQuery.azureMonitor!.metricNamespace,
|
||
resources: modernMetricsQuery.azureMonitor!.resources,
|
||
resourceUri: undefined,
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
|
||
it('correctly remove outdated fields', () => {
|
||
const result = migrateQuery(azureMonitorQueryV9_0);
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureMonitor: expect.objectContaining({
|
||
resources: modernMetricsQuery.azureMonitor!.resources,
|
||
}),
|
||
})
|
||
);
|
||
expect(result.azureMonitor).not.toHaveProperty('resourceGroup');
|
||
expect(result.azureMonitor).not.toHaveProperty('resourceName');
|
||
});
|
||
|
||
it('correctly adds the dashboardTime property', () => {
|
||
const result = migrateQuery({ ...azureMonitorQueryV9_0 });
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureLogAnalytics: expect.objectContaining({
|
||
dashboardTime: false,
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
});
|
||
|
||
it('should migrate a single resource for Logs', () => {
|
||
const q = {
|
||
...modernMetricsQuery,
|
||
azureLogAnalytics: {
|
||
...modernMetricsQuery.azureLogAnalytics,
|
||
resource: 'foo',
|
||
},
|
||
};
|
||
const result = migrateQuery(q);
|
||
expect(result.azureLogAnalytics?.resources).toEqual(['foo']);
|
||
});
|
||
|
||
it('correctly migrates intersectTime to dashboardTime', () => {
|
||
const query = modernMetricsQuery;
|
||
delete query.azureLogAnalytics?.dashboardTime;
|
||
const result = migrateQuery({
|
||
...query,
|
||
azureLogAnalytics: { ...query.azureLogAnalytics, intersectTime: true },
|
||
});
|
||
expect(result).toMatchObject(
|
||
expect.objectContaining({
|
||
azureLogAnalytics: expect.objectContaining({
|
||
dashboardTime: true,
|
||
}),
|
||
})
|
||
);
|
||
});
|
||
});
|