ISSUE:2806 - View traces/logs functionality across the product with new QB (#9207)

* fix: issue-2806 view traces/logs functionality across the product with new qb

* test: added test for getfilter

* test: updated tests
This commit is contained in:
Abhi kumar 2025-09-29 19:12:50 +05:30 committed by GitHub
parent 1b818dd05d
commit 8b21ba5db9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 301 additions and 13 deletions

View File

@ -634,4 +634,260 @@ describe('prepareQueryRangePayloadV5', () => {
}),
);
});
it('builds payload for builder queries with filters array but no filter expression', () => {
const props: GetQueryResultsProps = {
query: {
queryType: EQueryType.QUERY_BUILDER,
id: 'q8',
unit: undefined,
promql: [],
clickhouse_sql: [],
builder: {
queryData: [
baseBuilderQuery({
dataSource: DataSource.LOGS,
filter: { expression: '' },
filters: {
items: [
{
id: '1',
key: { key: 'service.name', type: 'string' },
op: '=',
value: 'payment-service',
},
{
id: '2',
key: { key: 'http.status_code', type: 'number' },
op: '>=',
value: 400,
},
{
id: '3',
key: { key: 'message', type: 'string' },
op: 'contains',
value: 'error',
},
],
op: 'AND',
},
}),
],
queryFormulas: [],
queryTraceOperator: [],
},
},
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
start,
end,
};
const result = prepareQueryRangePayloadV5(props);
expect(result.legendMap).toEqual({ A: 'Legend A' });
expect(result.queryPayload.compositeQuery.queries).toHaveLength(1);
const builderQuery = result.queryPayload.compositeQuery.queries.find(
(q) => q.type === 'builder_query',
) as QueryEnvelope;
const logSpec = builderQuery.spec as LogBuilderQuery;
expect(logSpec.name).toBe('A');
expect(logSpec.signal).toBe('logs');
expect(logSpec.filter).toEqual({
expression:
"service.name = 'payment-service' AND http.status_code >= 400 AND message contains 'error'",
});
});
it('uses filter.expression when only expression is provided', () => {
const props: GetQueryResultsProps = {
query: {
queryType: EQueryType.QUERY_BUILDER,
id: 'q9',
unit: undefined,
promql: [],
clickhouse_sql: [],
builder: {
queryData: [
baseBuilderQuery({
dataSource: DataSource.LOGS,
filter: { expression: 'http.status_code >= 500' },
filters: (undefined as unknown) as IBuilderQuery['filters'],
}),
],
queryFormulas: [],
queryTraceOperator: [],
},
},
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
start,
end,
};
const result = prepareQueryRangePayloadV5(props);
const builderQuery = result.queryPayload.compositeQuery.queries.find(
(q) => q.type === 'builder_query',
) as QueryEnvelope;
const logSpec = builderQuery.spec as LogBuilderQuery;
expect(logSpec.filter).toEqual({ expression: 'http.status_code >= 500' });
});
it('derives expression from filters when filter is undefined', () => {
const props: GetQueryResultsProps = {
query: {
queryType: EQueryType.QUERY_BUILDER,
id: 'q10',
unit: undefined,
promql: [],
clickhouse_sql: [],
builder: {
queryData: [
baseBuilderQuery({
dataSource: DataSource.LOGS,
filter: (undefined as unknown) as IBuilderQuery['filter'],
filters: {
items: [
{
id: '1',
key: { key: 'service.name', type: 'string' },
op: '=',
value: 'checkout',
},
],
op: 'AND',
},
}),
],
queryFormulas: [],
queryTraceOperator: [],
},
},
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
start,
end,
};
const result = prepareQueryRangePayloadV5(props);
const builderQuery = result.queryPayload.compositeQuery.queries.find(
(q) => q.type === 'builder_query',
) as QueryEnvelope;
const logSpec = builderQuery.spec as LogBuilderQuery;
expect(logSpec.filter).toEqual({ expression: "service.name = 'checkout'" });
});
it('prefers filter.expression over filters when both are present', () => {
const props: GetQueryResultsProps = {
query: {
queryType: EQueryType.QUERY_BUILDER,
id: 'q11',
unit: undefined,
promql: [],
clickhouse_sql: [],
builder: {
queryData: [
baseBuilderQuery({
dataSource: DataSource.LOGS,
filter: { expression: "service.name = 'frontend'" },
filters: {
items: [
{
id: '1',
key: { key: 'service.name', type: 'string' },
op: '=',
value: 'backend',
},
],
op: 'AND',
},
}),
],
queryFormulas: [],
queryTraceOperator: [],
},
},
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
start,
end,
};
const result = prepareQueryRangePayloadV5(props);
const builderQuery = result.queryPayload.compositeQuery.queries.find(
(q) => q.type === 'builder_query',
) as QueryEnvelope;
const logSpec = builderQuery.spec as LogBuilderQuery;
expect(logSpec.filter).toEqual({ expression: "service.name = 'frontend'" });
});
it('returns empty expression when neither filter nor filters provided', () => {
const props: GetQueryResultsProps = {
query: {
queryType: EQueryType.QUERY_BUILDER,
id: 'q12',
unit: undefined,
promql: [],
clickhouse_sql: [],
builder: {
queryData: [
baseBuilderQuery({
dataSource: DataSource.LOGS,
filter: (undefined as unknown) as IBuilderQuery['filter'],
filters: (undefined as unknown) as IBuilderQuery['filters'],
}),
],
queryFormulas: [],
queryTraceOperator: [],
},
},
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
start,
end,
};
const result = prepareQueryRangePayloadV5(props);
const builderQuery = result.queryPayload.compositeQuery.queries.find(
(q) => q.type === 'builder_query',
) as QueryEnvelope;
const logSpec = builderQuery.spec as LogBuilderQuery;
expect(logSpec.filter).toEqual({ expression: '' });
});
it('returns empty expression when filters provided with empty items', () => {
const props: GetQueryResultsProps = {
query: {
queryType: EQueryType.QUERY_BUILDER,
id: 'q13',
unit: undefined,
promql: [],
clickhouse_sql: [],
builder: {
queryData: [
baseBuilderQuery({
dataSource: DataSource.LOGS,
filter: { expression: '' },
filters: { items: [], op: 'AND' },
}),
],
queryFormulas: [],
queryTraceOperator: [],
},
},
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
start,
end,
};
const result = prepareQueryRangePayloadV5(props);
const builderQuery = result.queryPayload.compositeQuery.queries.find(
(q) => q.type === 'builder_query',
) as QueryEnvelope;
const logSpec = builderQuery.spec as LogBuilderQuery;
expect(logSpec.filter).toEqual({ expression: '' });
});
});

View File

@ -1,5 +1,6 @@
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable sonarjs/no-identical-functions */
import { convertFiltersToExpression } from 'components/QueryBuilderV2/utils';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
@ -14,6 +15,7 @@ import {
BaseBuilderQuery,
FieldContext,
FieldDataType,
Filter,
FunctionName,
GroupByKey,
Having,
@ -111,6 +113,23 @@ function isDeprecatedField(fieldName: string): boolean {
);
}
function getFilter(queryData: IBuilderQuery): Filter {
const { filter } = queryData;
if (filter?.expression) {
return {
expression: filter.expression,
};
}
if (queryData.filters && queryData.filters?.items?.length > 0) {
return convertFiltersToExpression(queryData.filters);
}
return {
expression: '',
};
}
function createBaseSpec(
queryData: IBuilderQuery,
requestType: RequestType,
@ -124,7 +143,7 @@ function createBaseSpec(
return {
stepInterval: queryData?.stepInterval || null,
disabled: queryData.disabled,
filter: queryData?.filter?.expression ? queryData.filter : undefined,
filter: getFilter(queryData),
groupBy:
queryData.groupBy?.length > 0
? queryData.groupBy.map(

View File

@ -42,18 +42,31 @@ export function useNavigateToExplorer(): (
builder: {
...widgetQuery.builder,
queryData: widgetQuery.builder.queryData
.map((item) => ({
...item,
dataSource,
aggregateOperator: MetricAggregateOperator.NOOP,
filters: {
...item.filters,
items: [...(item.filters?.items || []), ...selectedFilters],
op: item.filters?.op || 'AND',
},
groupBy: [],
disabled: false,
}))
.map((item) => {
// filter out filters with unique ids
const seen = new Set();
const filterItems = [
...(item.filters?.items || []),
...selectedFilters,
].filter((item) => {
if (seen.has(item.id)) return false;
seen.add(item.id);
return true;
});
return {
...item,
dataSource,
aggregateOperator: MetricAggregateOperator.NOOP,
filters: {
...item.filters,
items: filterItems,
op: item.filters?.op || 'AND',
},
groupBy: [],
disabled: false,
};
})
.slice(0, 1),
queryFormulas: [],
},