Compare commits

...

10 Commits

Author SHA1 Message Date
Gaurav Tewari
2b53724a2c Revert "refactor(options-menu): fetch keys with the field keys hook"
This reverts commit 7cb1985c31.
2026-10-29 00:56:11 +05:30
Gaurav Tewari
7cb1985c31 refactor(options-menu): fetch keys with the field keys hook
Assisted-by: Claude Opus 5.5
2026-10-29 00:41:44 +05:30
Gaurav Tewari
2a7b9a76b9 refactor(preferences): key slices by source page only
Assisted-by: Claude Opus 5.5
2026-10-28 23:36:46 +05:30
Gaurav Tewari
79100b1f21 refactor(preferences): rename preference key to preference
Assisted-by: Claude Opus 5.5
2026-10-28 23:10:41 +05:30
Gaurav Tewari
424a66faf0 refactor(options-menu): rename data source to signal
Assisted-by: Claude Opus 5.5
2026-10-28 22:15:51 +05:30
Gaurav Tewari
bdf6987923 refactor(options-menu): branch on source page
Assisted-by: Claude Opus 5.5
2026-10-28 22:06:27 +05:30
Gaurav Tewari
2023e374a2 Merge remote-tracking branch 'origin/main' into refactor/saved-view-source-page
# Conflicts:
#	frontend/src/components/ExplorerCard/ExplorerCard.tsx
#	frontend/src/container/ExplorerOptions/ExplorerOptions.tsx
2026-10-28 21:41:46 +05:30
Gaurav Tewari
ea26449143 refactor(preferences): key preference slices by source page and panel type
Assisted-by: Claude Opus 5.5
2026-10-28 19:07:02 +05:30
Gaurav Tewari
fd8cf966cf refactor(preferences): key preference slices by saved view source
Assisted-by: Claude Opus 5.5
2026-10-28 18:53:50 +05:30
Gaurav Tewari
2225bc445d refactor(saved-views): key options menu and saved view options by source dto
Assisted-by: Claude Opus 5.5
2026-10-28 18:48:02 +05:30
30 changed files with 274 additions and 201 deletions

View File

@@ -4,6 +4,7 @@ import { Button } from '@signozhq/ui/button';
import { Drawer, Tooltip } from 'antd';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import LogStateIndicator from 'components/Logs/LogStateIndicator/LogStateIndicator';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
@@ -147,7 +148,7 @@ function LogDetailInner({
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Switch } from '@signozhq/ui/switch';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import FieldsSelector from 'components/FieldsSelector';
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
import { MAX_LOGS_LIST_SIZE } from 'constants/liveTail';
@@ -54,7 +55,7 @@ function LiveLogsContainer({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -5,6 +5,7 @@ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
import { toast } from '@signozhq/ui/sonner';
import { Card } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
import ListLogView from 'components/Logs/ListLogView';
@@ -30,7 +31,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
import { useEventSource } from 'providers/EventSource';
// interfaces
import { ILog } from 'types/api/logs/log';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { StringOperators } from 'types/common/queryBuilder';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import { getAbsoluteUrl } from 'utils/basePath';
@@ -68,7 +69,7 @@ function LiveLogsList({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: StringOperators.NOOP,
});

View File

@@ -1,6 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { Button, Skeleton } from 'antd';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import RawLogView from 'components/Logs/RawLogView';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { LOCALSTORAGE } from 'constants/localStorage';
@@ -16,7 +17,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import useUrlQuery from 'hooks/useUrlQuery';
import { ILog } from 'types/api/logs/log';
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import { StringOperators } from 'types/common/queryBuilder';
import { withBasePath } from 'utils/basePath';
import { useContextLogData } from './useContextLogData';
@@ -45,7 +46,7 @@ function ContextLogRenderer({
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -5,6 +5,7 @@ import { Virtuoso, VirtuosoHandle } from 'react-virtuoso';
import { toast } from '@signozhq/ui/sonner';
import { Card } from 'antd';
import logEvent from 'api/common/logEvent';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants';
@@ -77,7 +78,7 @@ function LogsExplorerList({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator:
currentStagedQueryData?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -1,6 +1,7 @@
import { ReactNode, useState } from 'react';
import { Switch } from '@signozhq/ui/switch';
import { Typography } from '@signozhq/ui/typography';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import DownloadOptionsMenu from 'components/DownloadOptionsMenu/DownloadOptionsMenu';
import FieldsSelector from 'components/FieldsSelector';
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
@@ -28,7 +29,7 @@ function LogsActionsContainer({
}): JSX.Element {
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.LOGS_LIST_OPTIONS,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: listQuery?.aggregateOperator || StringOperators.NOOP,
});

View File

@@ -4,7 +4,7 @@ import { useGetQueryKeySuggestions } from 'hooks/querySuggestions/useGetQueryKey
import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { DataSource } from 'types/common/queryBuilder';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import useOptionsMenu from '../useOptionsMenu';
@@ -32,7 +32,7 @@ describe('useOptionsMenu', () => {
});
(usePreferenceContext as jest.Mock).mockReturnValue({
traces: {
[SavedviewtypesSourceDTO.traces]: {
preferences: {
columns: [],
formatting: {
@@ -44,7 +44,7 @@ describe('useOptionsMenu', () => {
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
},
logs: {
[SavedviewtypesSourceDTO.logs]: {
preferences: {
columns: [],
formatting: {
@@ -66,7 +66,7 @@ describe('useOptionsMenu', () => {
(useQueries as jest.Mock).mockReturnValue([]);
});
it('does not show isRoot or isEntryPoint in column options when dataSource is TRACES', () => {
it('does not show isRoot or isEntryPoint in column options when sourcePage is traces', () => {
// Mock the query key suggestions to return data including isRoot and isEntryPoint
(useGetQueryKeySuggestions as jest.Mock).mockReturnValue({
data: {
@@ -108,7 +108,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'count',
}),
);
@@ -127,7 +127,7 @@ describe('useOptionsMenu', () => {
expect(optionNames).toContain('serviceName');
});
it('does not show body in column options when dataSource is METRICS', () => {
it('does not show body in column options when sourcePage is metrics', () => {
// Mock the query key suggestions to return data including body
(useGetQueryKeySuggestions as jest.Mock).mockReturnValue({
data: {
@@ -163,7 +163,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.METRICS,
sourcePage: SavedviewtypesSourceDTO.metrics,
aggregateOperator: 'count',
}),
);
@@ -180,7 +180,7 @@ describe('useOptionsMenu', () => {
expect(optionNames).toContain('value');
});
it('does not show body in column options when dataSource is LOGS', () => {
it('does not show body in column options when sourcePage is logs', () => {
// Mock the query key suggestions to return data including body
(useGetQueryKeySuggestions as jest.Mock).mockReturnValue({
data: {
@@ -216,7 +216,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -275,12 +275,12 @@ describe('useOptionsMenu', () => {
isFetching: false,
});
(usePreferenceContext as jest.Mock).mockReturnValue({
traces: {
[SavedviewtypesSourceDTO.traces]: {
preferences: { columns: [], formatting: baseFormatting },
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
},
logs: {
[SavedviewtypesSourceDTO.logs]: {
preferences: { columns: seedColumns, formatting: baseFormatting },
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
@@ -291,7 +291,7 @@ describe('useOptionsMenu', () => {
it('reorders by composite IDs — preserves both same-name variants distinctly', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -322,7 +322,7 @@ describe('useOptionsMenu', () => {
it('reorder ignores ids that are not columns (e.g. state-indicator) and skips unknown ids', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -354,7 +354,7 @@ describe('useOptionsMenu', () => {
it('removes only the variant whose composite ID matches', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -375,7 +375,7 @@ describe('useOptionsMenu', () => {
it('removing by a non-matching composite ID is a no-op (filter returns the full list)', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
aggregateOperator: 'count',
}),
);
@@ -395,7 +395,7 @@ describe('useOptionsMenu', () => {
isFetching: false,
});
(usePreferenceContext as jest.Mock).mockReturnValue({
traces: {
[SavedviewtypesSourceDTO.traces]: {
preferences: {
columns: [
{ name: 'body', fieldContext: 'log' },
@@ -407,7 +407,7 @@ describe('useOptionsMenu', () => {
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
},
logs: {
[SavedviewtypesSourceDTO.logs]: {
preferences: { columns: [], formatting: {} },
updateColumns: mockUpdateColumns,
updateFormatting: mockUpdateFormatting,
@@ -416,7 +416,7 @@ describe('useOptionsMenu', () => {
const { result } = renderHook(() =>
useOptionsMenu({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'count',
}),
);

View File

@@ -1,3 +1,4 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { DataSource } from 'types/common/queryBuilder';
@@ -13,6 +14,14 @@ export const defaultOptionsQuery: OptionsQuery = {
fontSize: FontSize.SMALL,
};
export const SOURCE_PAGE_SIGNAL: Record<SavedviewtypesSourceDTO, DataSource> = {
[SavedviewtypesSourceDTO.logs]: DataSource.LOGS,
[SavedviewtypesSourceDTO.traces]: DataSource.TRACES,
[SavedviewtypesSourceDTO.metrics]: DataSource.METRICS,
[SavedviewtypesSourceDTO.meter]: DataSource.METRICS,
[SavedviewtypesSourceDTO.ai_observability]: DataSource.TRACES,
};
export const EXCLUDED_COLUMNS: Record<DataSource, string[]> = {
[DataSource.TRACES]: ['body', 'isRoot', 'isEntryPoint'],
[DataSource.METRICS]: ['body'],

View File

@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/cognitive-complexity */
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueries } from 'react-query';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { getKeySuggestions } from 'api/querySuggestions/getKeySuggestions';
import { TelemetryFieldKey } from 'api/v5/v5';
import { AxiosResponse } from 'axios';
@@ -10,7 +11,8 @@ import { useNotifications } from 'hooks/useNotifications';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { has } from 'lodash-es';
import { AllTraceFilterKeyValue } from 'constants/traceFilterKeys';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { usePreferenceSlice } from 'providers/preferences/context/usePreferenceSlice';
import { toPreferenceSourcePage } from 'providers/preferences/utils';
import {
QueryKeyRequestProps,
QueryKeySuggestionsResponseProps,
@@ -20,13 +22,13 @@ import {
FieldDataType,
SignalType,
} from 'types/api/v5/queryRange';
import { DataSource } from 'types/common/queryBuilder';
import {
defaultLogsSelectedColumns,
defaultOptionsQuery,
defaultTraceSelectedColumns,
EXCLUDED_COLUMNS,
SOURCE_PAGE_SIGNAL,
URL_OPTIONS,
} from './constants';
import {
@@ -40,7 +42,7 @@ import { buildCompositeKey, getOptionsFromKeys } from './utils';
interface UseOptionsMenuProps {
storageKey?: string;
dataSource: DataSource;
sourcePage: SavedviewtypesSourceDTO;
aggregateOperator?: string;
initialOptions?: InitialOptions;
}
@@ -52,15 +54,16 @@ interface UseOptionsMenu {
}
const useOptionsMenu = ({
dataSource,
sourcePage,
// aggregateOperator,
initialOptions = {},
}: UseOptionsMenuProps): UseOptionsMenu => {
const signal = SOURCE_PAGE_SIGNAL[sourcePage];
const { notifications } = useNotifications();
const prefCtx = usePreferenceContext();
// TODO: send null to updateColumns and updateFormatting if dataSource is not logs or traces
const slice = dataSource === DataSource.TRACES ? prefCtx.traces : prefCtx.logs;
const { preferences, updateColumns, updateFormatting } = slice;
const { preferences, updateColumns, updateFormatting } = usePreferenceSlice(
toPreferenceSourcePage(sourcePage),
);
const [searchText, setSearchText] = useState<string>('');
const [isFocused, setIsFocused] = useState<boolean>(false);
@@ -68,10 +71,10 @@ const useOptionsMenu = ({
const initialQueryParamsV5: QueryKeyRequestProps = useMemo(
() => ({
signal: dataSource,
signal,
searchText: '',
}),
[dataSource],
[signal],
);
const { query: optionsQuery, redirectWithQuery: redirectWithOptionsData } =
@@ -127,7 +130,7 @@ const useOptionsMenu = ({
?.map((column) => attributesData.find(({ name }) => name === column))
.filter((e) => !!e) || []) as TelemetryFieldKey[];
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
initialSelected = initialSelected
?.map((col) => {
if (col && Object.keys(AllTraceFilterKeyValue).includes(col?.name)) {
@@ -157,7 +160,7 @@ const useOptionsMenu = ({
isFetchedInitialAttributes,
initialOptions?.selectColumns,
initialAttributesResult,
dataSource,
sourcePage,
]);
const {
@@ -195,7 +198,7 @@ const useOptionsMenu = ({
fieldDataType: e.fieldDataType,
}));
}
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
return defaultTraceSelectedColumns.map((e) => ({
...e,
name: e.name,
@@ -203,16 +206,16 @@ const useOptionsMenu = ({
}
return [];
}, [dataSource, searchedAttributesDataV5?.data.data.keys]);
}, [sourcePage, searchedAttributesDataV5?.data.data.keys]);
const initialOptionsQuery: OptionsQuery = useMemo(() => {
let defaultColumns: TelemetryFieldKey[] = defaultOptionsQuery.selectColumns;
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
defaultColumns = defaultTraceSelectedColumns.map((e) => ({
...e,
name: e.name,
}));
} else if (dataSource === DataSource.LOGS) {
} else if (sourcePage === SavedviewtypesSourceDTO.logs) {
// eslint-disable-next-line sonarjs/no-identical-functions
defaultColumns = defaultLogsSelectedColumns.map((e) => ({
...e,
@@ -232,7 +235,7 @@ const useOptionsMenu = ({
...initialOptions,
selectColumns: finalSelectColumns,
};
}, [dataSource, initialOptions, initialSelectedColumns]);
}, [sourcePage, initialOptions, initialSelectedColumns]);
const selectedColumnKeys = useMemo(
() => preferences?.columns?.map(({ name }) => name) || [],
@@ -241,7 +244,7 @@ const useOptionsMenu = ({
const optionsFromAttributeKeys = useMemo(() => {
const filteredAttributeKeys = searchedAttributeKeys.filter((item) => {
const exclusions = EXCLUDED_COLUMNS[dataSource];
const exclusions = EXCLUDED_COLUMNS[signal];
if (exclusions) {
return !exclusions.includes(item.name);
}
@@ -249,7 +252,7 @@ const useOptionsMenu = ({
});
return getOptionsFromKeys(filteredAttributeKeys, selectedColumnKeys);
}, [dataSource, searchedAttributeKeys, selectedColumnKeys]);
}, [signal, searchedAttributeKeys, selectedColumnKeys]);
const handleRedirectWithOptionsData = useCallback(
(newQueryData: OptionsQuery) => {
@@ -285,7 +288,10 @@ const useOptionsMenu = ({
buildCompositeKey(f.name, f.fieldContext, f.fieldDataType) !== columnKey,
);
if (!newSelectedColumns?.length && dataSource !== DataSource.LOGS) {
if (
!newSelectedColumns?.length &&
sourcePage !== SavedviewtypesSourceDTO.logs
) {
notifications.error({
message: 'There must be at least one selected column',
});
@@ -294,7 +300,7 @@ const useOptionsMenu = ({
updateColumns(newSelectedColumns || []);
},
[dataSource, notifications, preferences, updateColumns],
[sourcePage, notifications, preferences, updateColumns],
);
const handleFormatChange = useCallback(

View File

@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
import { Button } from '@signozhq/ui/button';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import ROUTES from 'constants/routes';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { usePreferenceSlice } from 'providers/preferences/context/usePreferenceSlice';
import SavedViewsHeader from '../SavedViewsHeader';
import { mockSavedViewsApi } from './savedViewsApiMock';
@@ -28,7 +28,7 @@ const errors = {
};
function Harness(): JSX.Element {
const { traces } = usePreferenceContext();
const traces = usePreferenceSlice(SavedviewtypesSourceDTO.traces);
const [headerKey, setHeaderKey] = useState(0);
const columns = traces.preferences?.columns ?? [];

View File

@@ -9,19 +9,16 @@ import {
buildSavedViewDeletePermission,
buildSavedViewUpdatePermission,
} from 'lib/authz/hooks/useAuthZ/permissions/saved-view.permissions';
import { DataSource } from 'types/common/queryBuilder';
import { SavedViewRowActionConfig, SaveViewModalMode } from './types';
export const SAVED_VIEW_SCHEMA_VERSION = SavedviewtypesSchemaVersionDTO.v2;
// Only the logs and traces explorers have columns and formatting to save.
export const SAVED_VIEW_OPTIONS_DATA_SOURCE: Partial<
Record<SavedviewtypesSourceDTO, DataSource>
> = {
[SavedviewtypesSourceDTO.logs]: DataSource.LOGS,
[SavedviewtypesSourceDTO.traces]: DataSource.TRACES,
};
export const SAVED_VIEW_OPTIONS_SOURCES: SavedviewtypesSourceDTO[] = [
SavedviewtypesSourceDTO.logs,
SavedviewtypesSourceDTO.traces,
];
// What selecting a view writes to the url, so clearing it removes exactly these.
export const SAVED_VIEW_URL_PARAMS = [

View File

@@ -8,10 +8,9 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useGetSavedViewParams } from 'hooks/saveViews/useGetSavedViewParams';
import { buildSavedViewReadPermission } from 'lib/authz/hooks/useAuthZ/permissions/saved-view.permissions';
import { useAuthZ } from 'lib/authz/hooks/useAuthZ/useAuthZ';
import { DataSource } from 'types/common/queryBuilder';
import { toAPIError } from 'utils/errorUtils';
import { SAVED_VIEW_OPTIONS_DATA_SOURCE } from '../constants';
import { SAVED_VIEW_OPTIONS_SOURCES } from '../constants';
import { UseActiveSavedViewResult } from '../types';
import { hasUnsavedViewChanges } from '../utils/hasUnsavedViewChanges';
@@ -21,10 +20,8 @@ export function useActiveSavedView(
const { viewKey } = useGetSavedViewParams();
const { stagedQuery, panelType } = useQueryBuilder();
const optionsDataSource = SAVED_VIEW_OPTIONS_DATA_SOURCE[source];
const { options } = useOptionsMenu({
dataSource: optionsDataSource ?? DataSource.LOGS,
});
const hasOptions = SAVED_VIEW_OPTIONS_SOURCES.includes(source);
const { options } = useOptionsMenu({ sourcePage: source });
const { isLoading: isCheckingRead, deniedPermissions } = useAuthZ(
[buildSavedViewReadPermission(viewKey)],
@@ -63,7 +60,8 @@ export function useActiveSavedView(
view,
stagedQuery,
panelType: panelType ?? PANEL_TYPES.LIST,
options: optionsDataSource ? options : undefined,
options: hasOptions ? options : undefined,
sourcePage: source,
});
}, [
view,
@@ -71,7 +69,8 @@ export function useActiveSavedView(
isStagedQueryCurrent,
stagedQuery,
panelType,
optionsDataSource,
hasOptions,
source,
options,
]);

View File

@@ -20,13 +20,13 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useGetSavedViewParams } from 'hooks/saveViews/useGetSavedViewParams';
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceSlice } from 'providers/preferences/context/usePreferenceSlice';
import { toPreferenceSourcePage } from 'providers/preferences/utils';
import { toAPIError } from 'utils/errorUtils';
import { getUnstableCurrentSearchParams } from 'utils/getUnstableCurrentSearchParams';
import {
SAVED_VIEW_OPTIONS_DATA_SOURCE,
SAVED_VIEW_OPTIONS_SOURCES,
SAVED_VIEW_SCHEMA_VERSION,
SAVED_VIEW_TOAST_POSITION,
SAVED_VIEW_URL_PARAMS,
@@ -60,15 +60,9 @@ export function useSavedViewActions(
const { viewKey } = useGetSavedViewParams();
const logSavedViewEvent = useSavedViewLogEvent(source);
const optionsDataSource = SAVED_VIEW_OPTIONS_DATA_SOURCE[source];
const { options } = useOptionsMenu({
dataSource: optionsDataSource ?? DataSource.LOGS,
});
const preferences = usePreferenceContext();
const preferencesSlice =
optionsDataSource === DataSource.TRACES
? preferences.traces
: preferences.logs;
const hasOptions = SAVED_VIEW_OPTIONS_SOURCES.includes(source);
const { options } = useOptionsMenu({ sourcePage: source });
const preferencesSlice = usePreferenceSlice(toPreferenceSourcePage(source));
const { mutateAsync: createSavedView, isLoading: isCreating } =
useCreateSavedView();
@@ -86,10 +80,10 @@ export function useSavedViewActions(
query: stagedQuery ?? currentQuery,
panelType: panelType ?? PANEL_TYPES.LIST,
displayName,
options: optionsDataSource ? options : undefined,
options: hasOptions ? options : undefined,
color,
}),
[stagedQuery, currentQuery, panelType, optionsDataSource, options],
[stagedQuery, currentQuery, panelType, hasOptions, options],
);
const selectView = useCallback(
@@ -105,10 +99,10 @@ export function useSavedViewActions(
const revertView = useCallback(
(view: SavedviewtypesSavedViewDTO): void => {
if (optionsDataSource) {
if (hasOptions) {
const { columns, formatting } = getViewColumnsAndFormatting(
view.spec,
optionsDataSource,
source,
);
preferencesSlice.updateColumns(columns);
if (formatting) {
@@ -120,7 +114,7 @@ export function useSavedViewActions(
[SavedViewEventKeys.ViewId]: view.id,
});
},
[optionsDataSource, preferencesSlice, selectView, logSavedViewEvent],
[hasOptions, source, preferencesSlice, selectView, logSavedViewEvent],
);
// The query builder redirects only ever add params, so the url is rebuilt

View File

@@ -32,6 +32,7 @@ export interface HasUnsavedViewChangesArgs {
stagedQuery: Query | null;
panelType: PANEL_TYPES;
options?: SavedViewOptions;
sourcePage: SavedviewtypesSourceDTO;
}
export interface UseLastUsedViewResult {

View File

@@ -1,6 +1,7 @@
import {
SavedviewtypesSavedViewDTO,
SavedviewtypesSavedViewSpecDTO,
SavedviewtypesSourceDTO,
} from 'api/generated/services/sigNoz.schemas';
import {
initialQueriesMap,
@@ -120,6 +121,7 @@ describe('hasUnsavedViewChanges', () => {
view,
stagedQuery: staged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.traces,
options: { ...options, selectColumns: [columns[0]] },
}),
).toBe(true);
@@ -139,6 +141,7 @@ describe('hasUnsavedViewChanges', () => {
view: bare,
stagedQuery: stagedFrom(bare, DataSource.TRACES),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.traces,
options: { ...options, selectColumns: defaultTraceSelectedColumns },
}),
).toBe(false);
@@ -163,6 +166,7 @@ describe('hasUnsavedViewChanges', () => {
view: logsView,
stagedQuery: logsStaged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
options: { selectColumns: shownColumns, ...formatting },
}),
).toBe(false);
@@ -174,6 +178,7 @@ describe('hasUnsavedViewChanges', () => {
view: logsView,
stagedQuery: logsStaged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
options: { selectColumns: shownColumns, ...formatting, maxLines: 5 },
}),
).toBe(true);
@@ -193,6 +198,7 @@ describe('hasUnsavedViewChanges', () => {
view: bare,
stagedQuery: stagedFrom(bare, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
options: {
selectColumns: defaultLogsSelectedColumns,
format: 'table',
@@ -236,6 +242,7 @@ describe('hasUnsavedViewChanges', () => {
view: olderView,
stagedQuery: stagedFrom(olderView, DataSource.METRICS),
panelType: PANEL_TYPES.TIME_SERIES,
sourcePage: SavedviewtypesSourceDTO.metrics,
}),
).toBe(false);
});
@@ -283,6 +290,7 @@ describe('hasUnsavedViewChanges', () => {
view: storedView,
stagedQuery: stagedWithTag,
panelType: PANEL_TYPES.TIME_SERIES,
sourcePage: SavedviewtypesSourceDTO.metrics,
}),
).toBe(false);
});
@@ -322,6 +330,7 @@ describe('hasUnsavedViewChanges', () => {
view: noOrder,
stagedQuery: stagedFrom(noOrder, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(false);
});
@@ -337,6 +346,7 @@ describe('hasUnsavedViewChanges', () => {
view: noOrder,
stagedQuery: sorted,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(true);
@@ -346,6 +356,7 @@ describe('hasUnsavedViewChanges', () => {
view: saved,
stagedQuery: stagedFrom(saved, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(false);
});
@@ -362,6 +373,7 @@ describe('hasUnsavedViewChanges', () => {
view: legacy,
stagedQuery: stagedFrom(legacy, DataSource.LOGS),
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.logs,
}),
).toBe(false);
});
@@ -373,6 +385,7 @@ describe('hasUnsavedViewChanges', () => {
view,
stagedQuery: staged,
panelType: PANEL_TYPES.LIST,
sourcePage: SavedviewtypesSourceDTO.traces,
options: { ...options, ...formatting, maxLines: 5 },
}),
).toBe(false);

View File

@@ -1,4 +1,7 @@
import { SavedviewtypesSavedViewSpecDTO } from 'api/generated/services/sigNoz.schemas';
import {
SavedviewtypesSavedViewSpecDTO,
SavedviewtypesSourceDTO,
} from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import {
defaultLogsSelectedColumns,
@@ -7,7 +10,6 @@ import {
} from 'container/OptionsMenu/constants';
import { FontSize, LogViewMode } from 'container/OptionsMenu/types';
import { Preferences } from 'providers/preferences/types';
import { DataSource } from 'types/common/queryBuilder';
function withColumnNames(
columns: TelemetryFieldKey[] | undefined,
@@ -25,11 +27,11 @@ function withColumnNames(
// defaults filled in where the view has none.
export function getViewColumnsAndFormatting(
spec: SavedviewtypesSavedViewSpecDTO | undefined,
dataSource: DataSource,
sourcePage: SavedviewtypesSourceDTO,
): Preferences {
const selectedFields = spec?.selectedFields as TelemetryFieldKey[] | undefined;
if (dataSource === DataSource.LOGS) {
if (sourcePage === SavedviewtypesSourceDTO.logs) {
return {
columns: ensureLogsRequiredColumns(
withColumnNames(selectedFields) || defaultLogsSelectedColumns,
@@ -42,7 +44,7 @@ export function getViewColumnsAndFormatting(
},
};
}
if (dataSource === DataSource.TRACES) {
if (sourcePage === SavedviewtypesSourceDTO.traces) {
return { columns: selectedFields || defaultTraceSelectedColumns };
}
return { columns: [] };

View File

@@ -1,7 +1,9 @@
import { TelemetrytypesFieldContextDTO } from 'api/generated/services/sigNoz.schemas';
import {
SavedviewtypesSourceDTO,
TelemetrytypesFieldContextDTO,
} from 'api/generated/services/sigNoz.schemas';
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
import isEqual from 'lodash-es/isEqual';
import { DataSource } from 'types/common/queryBuilder';
import { HasUnsavedViewChangesArgs } from '../types';
import { getSavedViewQuery } from './getSavedViewQuery';
@@ -50,6 +52,7 @@ export function hasUnsavedViewChanges({
stagedQuery,
panelType,
options,
sourcePage,
}: HasUnsavedViewChangesArgs): boolean {
if (!stagedQuery) {
return false;
@@ -76,10 +79,9 @@ export function hasUnsavedViewChanges({
return false;
}
const dataSource = stagedQuery.builder.queryData[0]?.dataSource;
const { columns, formatting } = getViewColumnsAndFormatting(
view.spec,
dataSource,
sourcePage,
);
const toNames = (fields: { name?: string; key?: string }[]): unknown[] =>
fields.map((field) => field.name ?? field.key);
@@ -87,7 +89,7 @@ export function hasUnsavedViewChanges({
return true;
}
if (dataSource !== DataSource.LOGS || !formatting) {
if (sourcePage !== SavedviewtypesSourceDTO.logs || !formatting) {
return false;
}
return (

View File

@@ -11,6 +11,7 @@ import {
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import logEvent from 'api/common/logEvent';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import DownloadOptionsMenu from 'components/DownloadOptionsMenu/DownloadOptionsMenu';
import ListViewOrderBy from 'components/OrderBy/ListViewOrderBy';
import type { TableColumnDef } from 'components/TanStackTableView/types';
@@ -86,7 +87,7 @@ function ListView({
const { options, config } = useOptionsMenu({
storageKey: LOCALSTORAGE.TRACES_LIST_OPTIONS,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'count',
initialOptions: {
selectColumns: defaultSelectedColumns,

View File

@@ -73,7 +73,7 @@ function TracesExplorer(): JSX.Element {
const { options } = useOptionsMenu({
storageKey: LOCALSTORAGE.TRACES_LIST_OPTIONS,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
aggregateOperator: 'noop',
initialOptions: {
selectColumns: defaultSelectedColumns,

View File

@@ -1,15 +1,15 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LOCALSTORAGE } from 'constants/localStorage';
import { LogViewMode } from 'container/OptionsMenu/types';
import { LogViewMode, FontSize } from 'container/OptionsMenu/types';
import {
defaultLogsSelectedColumns,
defaultTraceSelectedColumns,
} from 'container/OptionsMenu/constants';
import { FontSize } from 'container/OptionsMenu/types';
import { render, screen, userEvent } from 'tests/test-utils';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceContext } from '../context/PreferenceContextProvider';
import { usePreferenceSlice } from '../context/usePreferenceSlice';
import { PreferenceSourcePage } from '../types';
const ROUTE_LOGS = '/logs';
const ROUTE_TRACES = '/traces';
@@ -38,14 +38,13 @@ function getLocalStorageJSON<T>(key: string): T | null {
}
function Consumer({
dataSource,
sourcePage,
testIdPrefix,
}: {
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
testIdPrefix: string;
}): JSX.Element {
const ctx = usePreferenceContext();
const slice = dataSource === DataSource.TRACES ? ctx.traces : ctx.logs;
const slice = usePreferenceSlice(sourcePage);
return (
<div>
@@ -58,7 +57,7 @@ function Consumer({
type="button"
onClick={(): void => {
const newCols: TelemetryFieldKey[] =
dataSource === DataSource.TRACES
sourcePage === SavedviewtypesSourceDTO.traces
? (defaultTraceSelectedColumns.slice(0, 1) as TelemetryFieldKey[])
: (defaultLogsSelectedColumns.slice(0, 1) as TelemetryFieldKey[]);
slice.updateColumns(newCols);
@@ -78,7 +77,10 @@ describe('PreferencesProvider integration', () => {
describe('Logs', () => {
it('loads defaults when no localStorage or url provided', () => {
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -101,7 +103,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -116,7 +121,10 @@ describe('PreferencesProvider integration', () => {
it('direct mode updateColumns persists to localStorage', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -142,7 +150,7 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix="logs" />,
<Consumer sourcePage={SavedviewtypesSourceDTO.logs} testIdPrefix="logs" />,
undefined,
{
initialRoute: initialEntry,
@@ -175,7 +183,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{
initialRoute: ROUTE_LOGS,
@@ -197,7 +208,7 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
function FormattingConsumer(): JSX.Element {
const { logs } = usePreferenceContext();
const logs = usePreferenceSlice(SavedviewtypesSourceDTO.logs);
return (
<button
data-testid="logs-update-formatting"
@@ -235,7 +246,10 @@ describe('PreferencesProvider integration', () => {
const initialEntry = `/logs?viewKey=${encodeURIComponent(viewKey)}`;
render(
<Consumer dataSource={DataSource.LOGS} testIdPrefix={TESTID_LOGS} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.logs}
testIdPrefix={TESTID_LOGS}
/>,
undefined,
{ initialRoute: initialEntry },
);
@@ -254,7 +268,10 @@ describe('PreferencesProvider integration', () => {
describe('Traces', () => {
it('loads defaults when no localStorage or url provided', () => {
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{
initialRoute: ROUTE_TRACES,
@@ -273,7 +290,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{
initialRoute: ROUTE_TRACES,
@@ -286,7 +306,10 @@ describe('PreferencesProvider integration', () => {
it('direct mode updateColumns persists to localStorage', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{
initialRoute: ROUTE_TRACES,
@@ -309,7 +332,10 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix="traces" />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix="traces"
/>,
undefined,
{
initialRoute: initialEntry,
@@ -338,7 +364,10 @@ describe('PreferencesProvider integration', () => {
});
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{ initialRoute: ROUTE_TRACES },
);
@@ -355,7 +384,7 @@ describe('PreferencesProvider integration', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
function TracesFormattingConsumer(): JSX.Element {
const { traces } = usePreferenceContext();
const traces = usePreferenceSlice(SavedviewtypesSourceDTO.traces);
return (
<button
data-testid="traces-update-formatting"
@@ -390,7 +419,10 @@ describe('PreferencesProvider integration', () => {
const initialEntry = `/traces?viewKey=${encodeURIComponent(viewKey)}`;
render(
<Consumer dataSource={DataSource.TRACES} testIdPrefix={TESTID_TRACES} />,
<Consumer
sourcePage={SavedviewtypesSourceDTO.traces}
testIdPrefix={TESTID_TRACES}
/>,
undefined,
{ initialRoute: initialEntry },
);

View File

@@ -1,6 +1,6 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { renderHook, waitFor } from '@testing-library/react';
import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
import { DataSource } from 'types/common/queryBuilder';
import logsLoaderConfig from '../configs/logsLoaderConfig';
import { usePreferenceLoader } from '../loader/usePreferenceLoader';
@@ -50,7 +50,7 @@ describe('usePreferenceLoader', () => {
const setReSync = jest.fn();
const { result } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
reSync: false,
setReSync,
}),
@@ -74,7 +74,7 @@ describe('usePreferenceLoader', () => {
const setReSync = jest.fn();
const { result } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
reSync: false,
setReSync,
}),
@@ -99,7 +99,7 @@ describe('usePreferenceLoader', () => {
// We'll unmount quickly to avoid the infinite loop
const { unmount } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
reSync: true,
setReSync,
}),
@@ -130,7 +130,7 @@ describe('usePreferenceLoader', () => {
const setReSync = jest.fn();
const { result } = renderHook(() =>
usePreferenceLoader({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
reSync: true,
setReSync,
}),

View File

@@ -9,7 +9,6 @@ import {
defaultTraceSelectedColumns,
ensureLogsRequiredColumns,
} from 'container/OptionsMenu/constants';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceSync } from '../sync/usePreferenceSync';
import { PreferenceMode } from '../types';
@@ -84,7 +83,7 @@ describe('usePreferenceSync in saved view mode', () => {
renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.DIRECT,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: undefined,
}),
);
@@ -101,7 +100,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.DIRECT,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: undefined,
}),
);
@@ -120,7 +119,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: 'view-1',
}),
);
@@ -147,7 +146,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: 'view-1',
}),
);
@@ -174,14 +173,14 @@ describe('usePreferenceSync in saved view mode', () => {
const withFields = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
savedViewId: 'with-fields',
}),
);
const withoutFields = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
savedViewId: 'without-fields',
}),
);
@@ -202,7 +201,7 @@ describe('usePreferenceSync in saved view mode', () => {
const { result } = renderHook(() =>
usePreferenceSync({
mode: PreferenceMode.SAVED_VIEW,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
savedViewId: 'missing',
}),
);

View File

@@ -1,15 +1,14 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { act } from 'react-dom/test-utils';
import { renderHook } from '@testing-library/react';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LogViewMode } from 'container/OptionsMenu/types';
import { FontSize } from 'container/OptionsMenu/types';
import { LogViewMode, FontSize } from 'container/OptionsMenu/types';
import {
FormattingOptions,
PreferenceMode,
Preferences,
} from 'providers/preferences/types';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceUpdater } from '../updater/usePreferenceUpdater';
@@ -62,7 +61,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -75,7 +74,7 @@ describe('usePreferenceUpdater', () => {
expect(typeof result.current.updateFormatting).toBe('function');
});
it('should call the logs updater for updateColumns with logs dataSource', () => {
it('should call the logs updater for updateColumns with the logs list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newColumns: TelemetryFieldKey[] = [
@@ -88,7 +87,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -108,7 +107,7 @@ describe('usePreferenceUpdater', () => {
expect(setReSync).toHaveBeenCalledWith(true);
});
it('should call the logs updater for updateFormatting with logs dataSource', () => {
it('should call the logs updater for updateFormatting with the logs list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newFormatting: FormattingOptions = {
@@ -120,7 +119,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -140,7 +139,7 @@ describe('usePreferenceUpdater', () => {
expect(setReSync).toHaveBeenCalledWith(true);
});
it('should call the traces updater for updateColumns with traces dataSource', () => {
it('should call the traces updater for updateColumns with the traces list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newColumns: TelemetryFieldKey[] = [
@@ -153,7 +152,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -173,7 +172,7 @@ describe('usePreferenceUpdater', () => {
expect(setReSync).toHaveBeenCalledWith(true);
});
it('should call the traces updater for updateFormatting with traces dataSource', () => {
it('should call the traces updater for updateFormatting with the traces list key', () => {
const setReSync = jest.fn();
const setSavedViewPreferences = jest.fn();
const newFormatting: FormattingOptions = {
@@ -185,7 +184,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,
@@ -211,7 +210,7 @@ describe('usePreferenceUpdater', () => {
const { result } = renderHook(() =>
usePreferenceUpdater({
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
mode: PreferenceMode.DIRECT,
preferences: mockPreferences,
setReSync,

View File

@@ -5,12 +5,12 @@ import React, {
useContext,
useMemo,
} from 'react';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import useUrlQuery from 'hooks/useUrlQuery';
import {
PreferenceContextValue,
PreferenceMode,
} from 'providers/preferences/types';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceSync } from '../sync/usePreferenceSync';
@@ -38,19 +38,19 @@ export function PreferenceContextProvider({
const logsSlice = usePreferenceSync({
mode: savedViewId ? PreferenceMode.SAVED_VIEW : PreferenceMode.DIRECT,
savedViewId: savedViewId || undefined,
dataSource: DataSource.LOGS,
sourcePage: SavedviewtypesSourceDTO.logs,
});
const tracesSlice = usePreferenceSync({
mode: savedViewId ? PreferenceMode.SAVED_VIEW : PreferenceMode.DIRECT,
savedViewId: savedViewId || undefined,
dataSource: DataSource.TRACES,
sourcePage: SavedviewtypesSourceDTO.traces,
});
const value = useMemo<PreferenceContextValue>(
() => ({
logs: logsSlice,
traces: tracesSlice,
[SavedviewtypesSourceDTO.logs]: logsSlice,
[SavedviewtypesSourceDTO.traces]: tracesSlice,
}),
[logsSlice, tracesSlice],
);

View File

@@ -0,0 +1,8 @@
import { PreferenceSlice, PreferenceSourcePage } from '../types';
import { usePreferenceContext } from './PreferenceContextProvider';
export function usePreferenceSlice(
sourcePage: PreferenceSourcePage,
): PreferenceSlice {
return usePreferenceContext()[sourcePage];
}

View File

@@ -1,12 +1,12 @@
import { useEffect, useState } from 'react';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { ensureLogsRequiredColumns } from 'container/OptionsMenu/constants';
import { has } from 'lodash-es';
import { DataSource } from 'types/common/queryBuilder';
import logsLoaderConfig from '../configs/logsLoaderConfig';
import tracesLoaderConfig from '../configs/tracesLoaderConfig';
import { FormattingOptions, Preferences } from '../types';
import { FormattingOptions, Preferences, PreferenceSourcePage } from '../types';
const migrateColumns = (columns: any): any =>
columns.map((column: any) => {
@@ -62,15 +62,21 @@ function logsPreferencesLoader(): {
function tracesPreferencesLoader(): {
columns: TelemetryFieldKey[];
} {
return preferencesLoader(tracesLoaderConfig);
const { columns } = preferencesLoader<Preferences>(tracesLoaderConfig);
return { columns };
}
const PREFERENCE_LOADERS: Record<PreferenceSourcePage, () => Preferences> = {
[SavedviewtypesSourceDTO.logs]: logsPreferencesLoader,
[SavedviewtypesSourceDTO.traces]: tracesPreferencesLoader,
};
export function usePreferenceLoader({
dataSource,
sourcePage,
reSync,
setReSync,
}: {
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
reSync: boolean;
setReSync: (value: boolean) => void;
}): {
@@ -78,17 +84,10 @@ export function usePreferenceLoader({
loading: boolean;
error: Error | null;
} {
const [preferences, setPreferences] = useState<Preferences | null>(() => {
if (dataSource === DataSource.LOGS) {
const { columns, formatting } = logsPreferencesLoader();
return { columns, formatting };
}
if (dataSource === DataSource.TRACES) {
const { columns } = tracesPreferencesLoader();
return { columns };
}
return null;
});
const loadSlicePreferences = PREFERENCE_LOADERS[sourcePage];
const [preferences, setPreferences] = useState<Preferences | null>(
loadSlicePreferences,
);
const [error, setError] = useState<Error | null>(null);
useEffect((): void => {
@@ -96,15 +95,7 @@ export function usePreferenceLoader({
setError(null);
try {
if (dataSource === DataSource.LOGS) {
const { columns, formatting } = logsPreferencesLoader();
setPreferences({ columns, formatting });
}
if (dataSource === DataSource.TRACES) {
const { columns } = tracesPreferencesLoader();
setPreferences({ columns });
}
setPreferences(loadSlicePreferences());
} catch (e) {
setError(e as Error);
} finally {
@@ -119,7 +110,7 @@ export function usePreferenceLoader({
if (reSync) {
loadPreferences();
}
}, [dataSource, reSync, setReSync]);
}, [loadSlicePreferences, reSync, setReSync]);
return { preferences, loading: false, error };
}

View File

@@ -3,19 +3,23 @@ import { useEffect, useRef, useState } from 'react';
import { useGetSavedView } from 'api/generated/services/saved-view';
import { TelemetryFieldKey } from 'api/v5/v5';
import { getViewColumnsAndFormatting } from 'container/SavedViews/utils/getViewColumnsAndFormatting';
import { DataSource } from 'types/common/queryBuilder';
import { usePreferenceLoader } from '../loader/usePreferenceLoader';
import { FormattingOptions, PreferenceMode, Preferences } from '../types';
import {
FormattingOptions,
PreferenceMode,
Preferences,
PreferenceSourcePage,
} from '../types';
import { usePreferenceUpdater } from '../updater/usePreferenceUpdater';
export function usePreferenceSync({
mode,
dataSource,
sourcePage,
savedViewId,
}: {
mode: PreferenceMode;
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
savedViewId: string | undefined;
}): {
preferences: Preferences | null;
@@ -41,25 +45,25 @@ export function usePreferenceSync({
if (view && view.id === savedViewId) {
if (seededViewId.current !== savedViewId) {
seededViewId.current = savedViewId;
setSavedViewPreferences(getViewColumnsAndFormatting(view.spec, dataSource));
setSavedViewPreferences(getViewColumnsAndFormatting(view.spec, sourcePage));
}
return;
}
seededViewId.current = undefined;
setSavedViewPreferences(getViewColumnsAndFormatting(undefined, dataSource));
}, [viewData, dataSource, savedViewId]);
setSavedViewPreferences(getViewColumnsAndFormatting(undefined, sourcePage));
}, [viewData, sourcePage, savedViewId]);
// We are using a reSync state because we have URL updates as well as local storage updates
// and we want to make sure we are always using the latest preferences
const [reSync, setReSync] = useState(false);
const { preferences, loading, error } = usePreferenceLoader({
dataSource,
sourcePage,
reSync,
setReSync,
});
const { updateColumns, updateFormatting } = usePreferenceUpdater({
dataSource,
sourcePage,
mode,
preferences,
setReSync,

View File

@@ -1,12 +1,17 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import { LogViewMode } from 'container/OptionsMenu/types';
import { FontSize } from 'container/OptionsMenu/types';
import { LogViewMode, FontSize } from 'container/OptionsMenu/types';
export enum PreferenceMode {
SAVED_VIEW = 'savedView',
DIRECT = 'direct',
}
// Only the logs and traces explorers keep column and formatting preferences.
export type PreferenceSourcePage =
| SavedviewtypesSourceDTO.logs
| SavedviewtypesSourceDTO.traces;
export interface PreferenceSlice {
preferences: Preferences | null;
loading: boolean;
@@ -15,10 +20,10 @@ export interface PreferenceSlice {
updateFormatting: (newFormatting: FormattingOptions) => void;
}
export interface PreferenceContextValue {
logs: PreferenceSlice;
traces: PreferenceSlice;
}
export type PreferenceContextValue = Record<
PreferenceSourcePage,
PreferenceSlice
>;
export interface FormattingOptions {
maxLines?: number;

View File

@@ -1,4 +1,5 @@
import { Dispatch, SetStateAction } from 'react';
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { TelemetryFieldKey } from 'api/v5/v5';
import {
defaultOptionsQuery,
@@ -6,48 +7,41 @@ import {
} from 'container/OptionsMenu/constants';
import { OptionsQuery } from 'container/OptionsMenu/types';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { DataSource } from 'types/common/queryBuilder';
import getLogsUpdaterConfig from '../configs/logsUpdaterConfig';
import getTracesUpdaterConfig from '../configs/tracesUpdaterConfig';
import { FormattingOptions, Preferences } from '../types';
const metricsUpdater = {
updateColumns: (): void => {}, // no-op for metrics
updateFormatting: (): void => {}, // no-op for metrics
};
import { FormattingOptions, Preferences, PreferenceSourcePage } from '../types';
const getUpdaterConfig = (
preferences: Preferences | null,
redirectWithOptionsData: (options: OptionsQuery) => void,
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>,
): Record<
DataSource,
PreferenceSourcePage,
{
updateColumns: (newColumns: TelemetryFieldKey[], mode: string) => void;
updateFormatting: (newFormatting: FormattingOptions, mode: string) => void;
}
> => ({
[DataSource.LOGS]: getLogsUpdaterConfig(
[SavedviewtypesSourceDTO.logs]: getLogsUpdaterConfig(
preferences,
redirectWithOptionsData,
setSavedViewPreferences,
),
[DataSource.TRACES]: getTracesUpdaterConfig(
[SavedviewtypesSourceDTO.traces]: getTracesUpdaterConfig(
redirectWithOptionsData,
setSavedViewPreferences,
),
[DataSource.METRICS]: metricsUpdater,
});
export function usePreferenceUpdater({
dataSource,
sourcePage,
mode,
preferences,
setReSync,
setSavedViewPreferences,
}: {
dataSource: DataSource;
sourcePage: PreferenceSourcePage;
mode: string;
preferences: Preferences | null;
setReSync: Dispatch<SetStateAction<boolean>>;
@@ -62,7 +56,7 @@ export function usePreferenceUpdater({
preferences,
redirectWithOptionsData,
setSavedViewPreferences,
)[dataSource];
)[sourcePage];
return {
updateColumns: (newColumns: TelemetryFieldKey[]): void => {

View File

@@ -0,0 +1,11 @@
import { SavedviewtypesSourceDTO } from 'api/generated/services/sigNoz.schemas';
import { PreferenceSourcePage } from './types';
export function toPreferenceSourcePage(
sourcePage: SavedviewtypesSourceDTO,
): PreferenceSourcePage {
return sourcePage === SavedviewtypesSourceDTO.traces
? SavedviewtypesSourceDTO.traces
: SavedviewtypesSourceDTO.logs;
}