Loki: Add scope filters to queries on the frontend (#104638)
* Add scopes to queries in DataSourceWithBackend * Remove Prometheus-specific solution * Readd prometheus support * move scopes reordering ti loki ds * Add tests and logQLScope feature flag * Move featureToggles to before/after each * Remove irrelevant file change
This commit is contained in:
@@ -181,6 +181,7 @@ class DataSourceWithBackend<
|
||||
if (datasource.uid?.length) {
|
||||
dsUIDs.add(datasource.uid);
|
||||
}
|
||||
|
||||
return {
|
||||
...(shouldApplyTemplateVariables ? this.applyTemplateVariables(q, request.scopedVars, request.filters) : q),
|
||||
datasource,
|
||||
|
||||
@@ -1809,6 +1809,162 @@ describe('LokiDatasource', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('scopes application', () => {
|
||||
let ds: LokiDatasource;
|
||||
let origBackendSrv: BackendSrv;
|
||||
|
||||
beforeEach(() => {
|
||||
origBackendSrv = getBackendSrv();
|
||||
ds = createLokiDatasource(templateSrvStub);
|
||||
// Enable the required feature toggles
|
||||
config.featureToggles.scopeFilters = true;
|
||||
config.featureToggles.logQLScope = true;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
setBackendSrv(origBackendSrv);
|
||||
// Reset feature toggles to false
|
||||
config.featureToggles.scopeFilters = false;
|
||||
config.featureToggles.logQLScope = false;
|
||||
});
|
||||
|
||||
it('should apply scopes to queries when feature toggles are enabled', async () => {
|
||||
const mockScopes = [
|
||||
{
|
||||
metadata: { name: 'test-scope' },
|
||||
spec: {
|
||||
title: 'Test Scope',
|
||||
type: 'test',
|
||||
description: 'Test scope description',
|
||||
category: 'test-category',
|
||||
filters: [
|
||||
{ key: 'environment', value: 'production', operator: 'equals' as const },
|
||||
{ key: 'service', value: 'api', operator: 'equals' as const },
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const query: DataQueryRequest<LokiQuery> = {
|
||||
...baseRequestOptions,
|
||||
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
|
||||
scopes: mockScopes,
|
||||
};
|
||||
|
||||
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
|
||||
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
|
||||
|
||||
await ds.query(query).pipe(take(1)).toPromise();
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
queries: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
scopes: [
|
||||
{ key: 'environment', value: 'production', operator: 'equals' },
|
||||
{ key: 'service', value: 'api', operator: 'equals' },
|
||||
],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should not apply scopes when feature toggles are disabled', async () => {
|
||||
// Disable the required feature toggles
|
||||
config.featureToggles.scopeFilters = false;
|
||||
config.featureToggles.logQLScope = false;
|
||||
|
||||
const mockScopes = [
|
||||
{
|
||||
metadata: { name: 'test-scope' },
|
||||
spec: {
|
||||
title: 'Test Scope',
|
||||
type: 'test',
|
||||
description: 'Test scope description',
|
||||
category: 'test-category',
|
||||
filters: [{ key: 'environment', value: 'production', operator: 'equals' as const }],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const query: DataQueryRequest<LokiQuery> = {
|
||||
...baseRequestOptions,
|
||||
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
|
||||
scopes: mockScopes,
|
||||
};
|
||||
|
||||
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
|
||||
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
|
||||
|
||||
await ds.query(query).pipe(take(1)).toPromise();
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
queries: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
scopes: undefined,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle empty scopes array', async () => {
|
||||
const query: DataQueryRequest<LokiQuery> = {
|
||||
...baseRequestOptions,
|
||||
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
|
||||
scopes: [],
|
||||
};
|
||||
|
||||
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
|
||||
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
|
||||
|
||||
await ds.query(query).pipe(take(1)).toPromise();
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
queries: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
scopes: [],
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle undefined scopes', async () => {
|
||||
const query: DataQueryRequest<LokiQuery> = {
|
||||
...baseRequestOptions,
|
||||
targets: [{ expr: '{job="grafana"}', refId: 'A' }],
|
||||
scopes: undefined,
|
||||
};
|
||||
|
||||
const fetchMock = jest.fn().mockReturnValue(of({ data: testLogsResponse }));
|
||||
setBackendSrv({ ...origBackendSrv, fetch: fetchMock });
|
||||
|
||||
await ds.query(query).pipe(take(1)).toPromise();
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
queries: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
scopes: undefined,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getQueryStats', () => {
|
||||
let ds: LokiDatasource;
|
||||
let query: LokiQuery;
|
||||
|
||||
@@ -299,7 +299,14 @@ export class LokiDatasource
|
||||
query(request: DataQueryRequest<LokiQuery>): Observable<DataQueryResponse> {
|
||||
const queries = request.targets
|
||||
.map(getNormalizedLokiQuery) // used to "fix" the deprecated `.queryType` prop
|
||||
.map((q) => ({ ...q, maxLines: q.maxLines ?? this.maxLines }));
|
||||
.map((q) => ({
|
||||
...q,
|
||||
maxLines: q.maxLines ?? this.maxLines,
|
||||
scopes:
|
||||
config.featureToggles.scopeFilters && config.featureToggles.logQLScope
|
||||
? request.scopes?.flatMap((scope) => scope.spec.filters)
|
||||
: undefined,
|
||||
}));
|
||||
|
||||
const fixedRequest: DataQueryRequest<LokiQuery> = {
|
||||
...request,
|
||||
|
||||
Reference in New Issue
Block a user