Compare commits

..

3 Commits

Author SHA1 Message Date
aniketio-ctrl
3370691219 Merge branch 'main' into fix/8403 2025-07-01 12:14:24 +05:30
aniket
e9ad673c09 fix(8403): throw error for no channel set 2025-07-01 12:12:04 +05:30
aniket
37db9964fb fix(8403): throw error for no channel set 2025-07-01 12:10:29 +05:30
9 changed files with 18 additions and 202 deletions

View File

@@ -98,7 +98,6 @@ interface QueryBuilderSearchV2Props {
hideSpanScopeSelector?: boolean;
// Determines whether to call onChange when a tag is closed
triggerOnChangeOnClose?: boolean;
skipQueryBuilderRedirect?: boolean;
}
export interface Option {
@@ -138,7 +137,6 @@ function QueryBuilderSearchV2(
operatorConfigKey,
hideSpanScopeSelector,
triggerOnChangeOnClose,
skipQueryBuilderRedirect,
} = props;
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
@@ -1040,11 +1038,7 @@ function QueryBuilderSearchV2(
})}
</Select>
{!hideSpanScopeSelector && (
<SpanScopeSelector
query={query}
onChange={onChange}
skipQueryBuilderRedirect={skipQueryBuilderRedirect}
/>
<SpanScopeSelector query={query} onChange={onChange} />
)}
</div>
);
@@ -1062,7 +1056,6 @@ QueryBuilderSearchV2.defaultProps = {
operatorConfigKey: undefined,
hideSpanScopeSelector: true,
triggerOnChangeOnClose: false,
skipQueryBuilderRedirect: false,
};
export default QueryBuilderSearchV2;

View File

@@ -23,7 +23,6 @@ interface SpanFilterConfig {
interface SpanScopeSelectorProps {
onChange?: (value: TagFilter) => void;
query?: IBuilderQuery;
skipQueryBuilderRedirect?: boolean;
}
const SPAN_FILTER_CONFIG: Record<SpanScope, SpanFilterConfig | null> = {
@@ -59,7 +58,6 @@ const SELECT_OPTIONS = [
function SpanScopeSelector({
onChange,
query,
skipQueryBuilderRedirect,
}: SpanScopeSelectorProps): JSX.Element {
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
const [selectedScope, setSelectedScope] = useState<SpanScope>(
@@ -81,7 +79,6 @@ function SpanScopeSelector({
if (hasFilter('isEntryPoint')) return SpanScope.ENTRYPOINT_SPANS;
return SpanScope.ALL_SPANS;
};
useEffect(() => {
let queryData = (currentQuery?.builder?.queryData || [])?.find(
(item) => item.queryName === query?.queryName,
@@ -130,10 +127,13 @@ function SpanScopeSelector({
},
}));
if (skipQueryBuilderRedirect && onChange && query) {
if (onChange && query) {
onChange({
...query.filters,
items: getUpdatedFilters([...query.filters.items], true),
items: getUpdatedFilters(
[...query.filters.items, ...newQuery.builder.queryData[0].filters.items],
true,
),
});
setSelectedScope(newScope);
@@ -156,7 +156,6 @@ function SpanScopeSelector({
SpanScopeSelector.defaultProps = {
onChange: undefined,
query: undefined,
skipQueryBuilderRedirect: false,
};
export default SpanScopeSelector;

View File

@@ -3,11 +3,9 @@ import {
render,
RenderResult,
screen,
within,
} from '@testing-library/react';
import { initialQueriesMap } from 'constants/queryBuilder';
import { QueryBuilderContext } from 'providers/QueryBuilder';
import { QueryClient, QueryClientProvider } from 'react-query';
import {
IBuilderQuery,
Query,
@@ -15,7 +13,6 @@ import {
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
import QueryBuilderSearchV2 from '../QueryBuilderSearchV2';
import SpanScopeSelector from '../SpanScopeSelector';
const mockRedirectWithQueryBuilderData = jest.fn();
@@ -51,14 +48,6 @@ const defaultQuery = {
},
};
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
},
},
});
const defaultQueryBuilderQuery: IBuilderQuery = {
...initialQueriesMap.traces.builder.queryData[0],
queryName: 'A',
@@ -87,7 +76,6 @@ const renderWithContext = (
initialQuery = defaultQuery,
onChangeProp?: (value: TagFilter) => void,
queryProp?: IBuilderQuery,
skipQueryBuilderRedirect = false,
): RenderResult =>
render(
<QueryBuilderContext.Provider
@@ -99,19 +87,12 @@ const renderWithContext = (
} as any
}
>
<SpanScopeSelector
onChange={onChangeProp}
query={queryProp}
skipQueryBuilderRedirect={skipQueryBuilderRedirect}
/>
<SpanScopeSelector onChange={onChangeProp} query={queryProp} />
</QueryBuilderContext.Provider>,
);
const selectOption = async (optionText: string): Promise<void> => {
const selector = within(screen.getByTestId('span-scope-selector')).getByRole(
'combobox',
);
const selector = screen.getByRole('combobox');
fireEvent.mouseDown(selector);
// Wait for dropdown to appear
@@ -283,7 +264,6 @@ describe('SpanScopeSelector', () => {
defaultQuery,
mockOnChange,
localQuery,
true,
);
expect(await screen.findByText('All Spans')).toBeInTheDocument();
@@ -303,7 +283,6 @@ describe('SpanScopeSelector', () => {
defaultQuery,
mockOnChange,
localQuery,
true,
);
expect(await screen.findByText('Root Spans')).toBeInTheDocument();
@@ -324,7 +303,6 @@ describe('SpanScopeSelector', () => {
defaultQuery,
mockOnChange,
localQuery,
true,
);
expect(await screen.findByText('Root Spans')).toBeInTheDocument();
@@ -346,7 +324,6 @@ describe('SpanScopeSelector', () => {
defaultQuery,
mockOnChange,
localQuery,
true,
);
expect(await screen.findByText('Root Spans')).toBeInTheDocument();
@@ -373,7 +350,6 @@ describe('SpanScopeSelector', () => {
defaultQuery,
mockOnChange,
localQuery,
true,
);
expect(await screen.findByText('Entrypoint Spans')).toBeInTheDocument();
@@ -385,60 +361,5 @@ describe('SpanScopeSelector', () => {
container.querySelector('span[title="All Spans"]'),
).toBeInTheDocument();
});
it('should not duplicate non-scope filters when changing span scope', async () => {
const query = {
...defaultQuery,
builder: {
...defaultQuery.builder,
queryData: [
{
...defaultQuery.builder.queryData[0],
filters: {
items: [createNonScopeFilter('service', 'checkout')],
op: 'AND',
},
},
],
},
};
render(
<QueryClientProvider client={queryClient}>
<QueryBuilderContext.Provider
value={
{
currentQuery: query,
redirectWithQueryBuilderData: mockRedirectWithQueryBuilderData,
} as any
}
>
<QueryBuilderSearchV2
query={query.builder.queryData[0] as any}
onChange={mockOnChange}
hideSpanScopeSelector={false}
/>
</QueryBuilderContext.Provider>
</QueryClientProvider>,
);
expect(await screen.findByText('All Spans')).toBeInTheDocument();
await selectOption('Entrypoint Spans');
expect(mockRedirectWithQueryBuilderData).toHaveBeenCalled();
const redirectQueryArg = mockRedirectWithQueryBuilderData.mock
.calls[0][0] as Query;
const { items } = redirectQueryArg.builder.queryData[0].filters;
// Count non-scope filters
const nonScopeFilters = items.filter(
(filter) => filter.key?.type !== 'spanSearchScope',
);
expect(nonScopeFilters).toHaveLength(1);
expect(nonScopeFilters).toContainEqual(
createNonScopeFilter('service', 'checkout'),
);
});
});
});

View File

@@ -142,7 +142,6 @@ function Filters({
}}
onChange={handleFilterChange}
hideSpanScopeSelector={false}
skipQueryBuilderRedirect
/>
{filteredSpanIds.length > 0 && (
<div className="pre-next-toggle">

View File

@@ -17,7 +17,6 @@ import { AppState } from 'store/reducers';
import { DataSource } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';
import DOCLINKS from 'utils/docLinks';
import { transformBuilderQueryFields } from 'utils/queryTransformers';
import TraceExplorerControls from '../Controls';
import { TracesLoading } from '../TraceLoading/TraceLoading';
@@ -40,22 +39,9 @@ function TracesView({ isFilterApplied }: TracesViewProps): JSX.Element {
QueryParams.pagination,
);
const transformedQuery = useMemo(
() =>
transformBuilderQueryFields(stagedQuery || initialQueriesMap.traces, {
orderBy: [
{
columnName: 'timestamp',
order: 'desc',
},
],
}),
[stagedQuery],
);
const { data, isLoading, isFetching, isError } = useGetQueryRange(
{
query: transformedQuery,
query: stagedQuery || initialQueriesMap.traces,
graphType: panelType || PANEL_TYPES.TRACE,
selectedTime: 'GLOBAL_TIME',
globalSelectedInterval: globalSelectedTime,

View File

@@ -594,53 +594,6 @@ describe('TracesExplorer - ', () => {
'http://localhost/trace/5765b60ba7cc4ddafe8bdaa9c1b4b246',
);
});
it('trace explorer - trace view should only send order by timestamp in the query', async () => {
let capturedPayload: QueryRangePayload;
const orderBy = [
{ columnName: 'id', order: 'desc' },
{ columnName: 'serviceName', order: 'desc' },
];
const defaultOrderBy = [{ columnName: 'timestamp', order: 'desc' }];
server.use(
rest.post(`${BASE_URL}/api/v4/query_range`, async (req, res, ctx) => {
const payload = await req.json();
capturedPayload = payload;
return res(ctx.status(200), ctx.json(queryRangeForTraceView));
}),
);
render(
<QueryBuilderContext.Provider
value={{
...qbProviderValue,
panelType: PANEL_TYPES.TRACE,
stagedQuery: {
...qbProviderValue.stagedQuery,
builder: {
...qbProviderValue.stagedQuery.builder,
queryData: [
{
...qbProviderValue.stagedQuery.builder.queryData[0],
orderBy,
},
],
},
},
}}
>
<TracesExplorer />
</QueryBuilderContext.Provider>,
);
await waitFor(() => {
expect(capturedPayload).toBeDefined();
expect(capturedPayload?.compositeQuery?.builderQueries?.A.orderBy).toEqual(
defaultOrderBy,
);
expect(
capturedPayload?.compositeQuery?.builderQueries?.A.orderBy,
).not.toEqual(orderBy);
});
});
it('test for explorer options', async () => {
const { getByText, getByTestId } = render(

View File

@@ -1,28 +0,0 @@
import { cloneDeep } from 'lodash-es';
import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData';
/**
* Transforms a query by modifying specific fields in the builder queries
* @param query - The original query object
* @param fieldOverrides - Partial object containing fields to override in each builder query
* @returns A new query object with the modified fields
*/
export const transformBuilderQueryFields = (
query: Query,
fieldOverrides: Partial<IBuilderQuery>,
): Query => {
// Create a deep copy of the query
const transformedQuery: Query = cloneDeep(query);
// Update the specified fields for each query in the builder
if (transformedQuery.builder?.queryData) {
transformedQuery.builder.queryData = transformedQuery.builder.queryData.map(
(queryItem) => ({
...queryItem,
...fieldOverrides,
}),
);
}
return transformedQuery;
};

View File

@@ -236,14 +236,7 @@ func (q *queryCache) FindMissingTimeRanges(orgID valuer.UUID, start, end, step i
func (q *queryCache) getCachedSeriesData(orgID valuer.UUID, cacheKey string) []*CachedSeriesData {
cacheableSeriesData := new(CacheableSeriesData)
tmpcacheableSeriesData := new(CacheableSeriesData)
err := q.cache.Get(context.TODO(), orgID, cacheKey, tmpcacheableSeriesData, true)
data, err := tmpcacheableSeriesData.MarshalBinary()
if err != nil {
zap.L().Error("error marshalling cacheable series data", zap.Error(err))
}
cacheableSeriesData.UnmarshalBinary(data)
err := q.cache.Get(context.TODO(), orgID, cacheKey, cacheableSeriesData, true)
if err != nil && !errors.Ast(err, errors.TypeNotFound) {
return nil
}
@@ -307,18 +300,11 @@ func (q *queryCache) MergeWithCachedSeriesDataV2(orgID valuer.UUID, cacheKey str
return newData
}
tmpcacheableSeriesData := new(CacheableSeriesData)
cacheableSeriesData := new(CacheableSeriesData)
err := q.cache.Get(context.TODO(), orgID, cacheKey, tmpcacheableSeriesData, true)
err := q.cache.Get(context.TODO(), orgID, cacheKey, cacheableSeriesData, true)
if err != nil && !errors.Ast(err, errors.TypeNotFound) {
return nil
}
data, err := tmpcacheableSeriesData.MarshalBinary()
if err != nil {
zap.L().Error("error marshalling cacheable series data", zap.Error(err))
}
cacheableSeriesData.UnmarshalBinary(data)
allData := append(cacheableSeriesData.Series, newData...)
sort.Slice(allData, func(i, j int) bool {

View File

@@ -353,6 +353,10 @@ func (m *Manager) EditRule(ctx context.Context, ruleStr string, id valuer.UUID)
return err
}
if len(channels) == 0 {
return errors.New("no channels found for this org, please set channels first")
}
for _, channel := range channels {
preferredChannels = append(preferredChannels, channel.Name)
}
@@ -536,6 +540,9 @@ func (m *Manager) CreateRule(ctx context.Context, ruleStr string) (*ruletypes.Ge
if err != nil {
return err
}
if len(channels) == 0 {
return errors.New("no channels found for this org, please set channels first")
}
for _, channel := range channels {
preferredChannels = append(preferredChannels, channel.Name)