Compare commits

..

2 Commits

Author SHA1 Message Date
Ashwin Bhatkal
c70866e4b3 fix(dashboards-list): never replace past the caret in DSL autocomplete (#12384)
Some checks are pending
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
cacheci / tests (push) Waiting to run
Release Drafter / update_release_draft (push) Waiting to run
getCaretContext resolved the active slot to a token's start index while
keeping replaceEnd at the caret, so a caret parked in the whitespace
before that token produced an inverted range (replaceStart > replaceEnd).
dslCompletionSource passes that range to CodeMirror as CompletionResult
from/to, and accepting a suggestion threw

  RangeError: Invalid change range 16 to 15 (in doc of length 36)

inside view.dispatch. The same inverted range made spliceAtCaret
duplicate the skipped character.

The replaced range is defined as ending at the caret, so clamp it there
and let the slot collapse to a plain insertion point.
2026-08-04 10:01:20 +00:00
Swapnil Nakade
34041be308 fix: adjust aggregation values for GCP metrics (#12391) 2026-08-04 09:41:14 +00:00
18 changed files with 117 additions and 515 deletions

View File

@@ -31,13 +31,6 @@ interface FieldsSelectorProps {
width?: number;
height?: number;
defaultPosition?: { x: number; y: number };
/**
* Caller-supplied field list. When provided, Other Fields offers exactly
* these and key discovery is skipped — use it when the set of selectable
* fields is known up front rather than fetched. Omit to discover keys from
* the API for `signal`.
*/
availableFields?: TelemetryFieldKey[];
}
type FieldsSelectorContentProps = Omit<FieldsSelectorProps, 'isOpen'>;
@@ -56,7 +49,6 @@ function FieldsSelectorContent({
width = DEFAULT_PANEL_WIDTH,
height,
defaultPosition,
availableFields,
}: FieldsSelectorContentProps): JSX.Element {
const resolvedHeight =
height ?? window.innerHeight - DEFAULT_PANEL_HEIGHT_OFFSET;
@@ -161,7 +153,6 @@ function FieldsSelectorContent({
addedFields={draftFields}
onAdd={handleAdd}
isAtLimit={isAtLimit}
availableFields={availableFields}
/>
{hasUnsavedChanges && (

View File

@@ -21,11 +21,6 @@ interface OtherFieldsProps {
addedFields: TelemetryFieldKey[];
onAdd: (field: TelemetryFieldKey) => void;
isAtLimit: boolean;
/**
* Caller-supplied field list. When provided, key discovery is skipped and
* these are filtered locally by the search term instead.
*/
availableFields?: TelemetryFieldKey[];
}
function OtherFields({
@@ -34,10 +29,7 @@ function OtherFields({
addedFields,
onAdd,
isAtLimit,
availableFields,
}: OtherFieldsProps): JSX.Element {
const useRegistry = Boolean(availableFields);
const { data, isFetching } = useGetQueryKeySuggestions(
{
signal,
@@ -49,34 +41,11 @@ function OtherFields({
signal,
debouncedInputValue,
],
enabled: !useRegistry,
enabled: true,
},
);
const otherFields: TelemetryFieldKey[] = useMemo(() => {
const addedIds = new Set(
addedFields.map((f) => f.key ?? buildCompositeKey(f.name, f.fieldContext)),
);
if (useRegistry && availableFields) {
const search = debouncedInputValue.trim().toLowerCase();
return availableFields
.filter((attr) => {
const id = attr.key ?? buildCompositeKey(attr.name, attr.fieldContext);
if (addedIds.has(id)) {
return false;
}
if (!search) {
return true;
}
return attr.name.toLowerCase().includes(search);
})
.map((attr) => ({
...attr,
key: attr.key ?? buildCompositeKey(attr.name, attr.fieldContext),
}));
}
const suggestions = Object.values(data?.data.data.keys || {}).flat();
// Normalize: synthesize `key` once so downstream reads can trust it.
const normalizedSuggestions: TelemetryFieldKey[] = suggestions.map(
@@ -88,12 +57,15 @@ function OtherFields({
fieldDataType: attr.fieldDataType,
}),
);
const addedIds = new Set(
addedFields.map((f) => f.key ?? buildCompositeKey(f.name, f.fieldContext)),
);
return normalizedSuggestions.filter(
(attr) => !addedIds.has(attr.key as string),
);
}, [data, addedFields, availableFields, debouncedInputValue, useRegistry]);
}, [data, addedFields]);
if (!useRegistry && isFetching) {
if (isFetching) {
return (
<div className={cx(styles.section, styles.sectionOther)}>
<div className={styles.sectionHeader}>OTHER FIELDS</div>

View File

@@ -11,8 +11,6 @@ export enum LOCALSTORAGE {
TRACES_LIST_OPTIONS = 'TRACES_LIST_OPTIONS',
GRAPH_VISIBILITY_STATES = 'GRAPH_VISIBILITY_STATES',
TRACES_LIST_COLUMNS = 'TRACES_LIST_COLUMNS',
/** AI Observability Trace View column visibility. */
AI_TRACE_VIEW_COLUMNS = 'AI_TRACE_VIEW_COLUMNS',
LOGS_LIST_COLUMNS = 'LOGS_LIST_COLUMNS',
LOGS_LIST_COLUMN_SIZING = 'LOGS_LIST_COLUMN_SIZING',
LOGGED_IN_USER_NAME = 'LOGGED_IN_USER_NAME',

View File

@@ -1,178 +0,0 @@
import { Typography } from '@signozhq/ui/typography';
import { LOCALSTORAGE } from 'constants/localStorage';
import {
BASE_TRACE_VIEW_COLUMNS,
renderTraceDurationCell,
TraceViewColumn,
} from 'container/TracesExplorer/TracesView/configs';
import { TraceViewColumnSelection } from 'container/TracesExplorer/TracesView/useTraceViewColumns';
function isBlank(value: unknown): boolean {
return value === undefined || value === null || value === '';
}
function renderCountCell(value: unknown): JSX.Element {
if (isBlank(value)) {
return <Typography></Typography>;
}
const count = Number(value);
return (
<Typography>
{Number.isFinite(count) ? count.toLocaleString() : String(value)}
</Typography>
);
}
function renderCostCell(value: unknown): JSX.Element {
if (isBlank(value)) {
return <Typography></Typography>;
}
const cost = Number(value);
return (
<Typography>
{Number.isFinite(cost) ? `$${cost.toFixed(4)}` : String(value)}
</Typography>
);
}
/**
* Trace-level gen_ai aggregates. The query-range response does not carry these
* yet, so they render as em dashes until the AI trace API lands — visible but
* empty is intentional, it lets the column set be reviewed ahead of the data.
*/
const AI_ONLY_COLUMNS: TraceViewColumn[] = [
{
field: {
name: 'input_tokens',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Input Tokens',
render: renderCountCell,
},
{
field: {
name: 'output_tokens',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Output Tokens',
render: renderCountCell,
},
{
field: {
name: 'total_tokens',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Total Tokens',
render: renderCountCell,
},
{
field: {
name: 'llm_call_count',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'LLM Calls',
render: renderCountCell,
},
{
field: {
name: 'tool_call_count',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Tool Calls',
render: renderCountCell,
},
{
field: {
name: 'distinct_tool_count',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Distinct Tools',
render: renderCountCell,
},
{
field: {
name: 'estimated_cost_usd',
fieldContext: 'trace',
fieldDataType: 'float64',
},
title: 'Est. Cost (USD)',
render: renderCostCell,
},
{
field: {
name: 'max_llm_latency_ns',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Max LLM Latency',
render: renderTraceDurationCell,
},
{
field: {
name: 'last_activity_time',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Last Activity',
},
{
field: { name: 'start_time', fieldContext: 'trace', fieldDataType: 'int64' },
title: 'Start Time',
},
{
field: { name: 'end_time', fieldContext: 'trace', fieldDataType: 'int64' },
title: 'End Time',
},
{
field: {
name: 'trace_duration_nano',
fieldContext: 'trace',
fieldDataType: 'int64',
},
title: 'Trace Duration',
render: renderTraceDurationCell,
},
{
field: { name: 'error_count', fieldContext: 'trace', fieldDataType: 'int64' },
title: 'Errors',
render: renderCountCell,
},
{
field: {
name: 'root_span_name',
fieldContext: 'trace',
fieldDataType: 'string',
},
title: 'Root Span Name',
},
{
field: { name: 'input', fieldContext: 'trace', fieldDataType: 'string' },
title: 'Input',
},
{
field: { name: 'output', fieldContext: 'trace', fieldDataType: 'string' },
title: 'Output',
},
];
export const AI_TRACE_VIEW_COLUMNS: TraceViewColumn[] = [
...BASE_TRACE_VIEW_COLUMNS,
...AI_ONLY_COLUMNS,
];
/**
* Hand this to `<TracesView columnSelection={…} />` to get the AI column set
* plus the Options → Edit columns picker. Module-level so its identity is
* stable across renders.
*/
export const AI_TRACE_VIEW_COLUMN_SELECTION: TraceViewColumnSelection = {
columns: AI_TRACE_VIEW_COLUMNS,
storageKey: LOCALSTORAGE.AI_TRACE_VIEW_COLUMNS,
defaultVisible: BASE_TRACE_VIEW_COLUMNS.map((column) => column.field.name),
};

View File

@@ -11,12 +11,4 @@
align-items: center;
gap: 4px;
cursor: pointer;
// Rendered as a <button> for keyboard access — strip the native chrome so it
// still reads as the inline text trigger it was.
padding: 0;
border: none;
background: none;
color: inherit;
font: inherit;
}

View File

@@ -5,7 +5,6 @@ import FieldsSelector from 'components/FieldsSelector';
import Controls, { ControlsProps } from 'container/Controls';
import { OptionsMenuConfig } from 'container/OptionsMenu/types';
import useQueryPagination from 'hooks/queryPagination/useQueryPagination';
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
import { DataSource } from 'types/common/queryBuilder';
import styles from './Controls.module.scss';
@@ -16,7 +15,6 @@ function TraceExplorerControls({
perPageOptions,
config,
showSizeChanger = true,
availableFields,
}: TraceExplorerControlsProps): JSX.Element | null {
const { t } = useTranslation(['trace']);
const [isFieldsSelectorOpen, setIsFieldsSelectorOpen] = useState(false);
@@ -32,15 +30,13 @@ function TraceExplorerControls({
<div className={styles.container}>
{config?.fieldsSelector && (
<>
<button
type="button"
<div
className={styles.optionsTrigger}
onClick={(): void => setIsFieldsSelectorOpen(true)}
data-testid="trace-view-options-trigger"
>
{t('options_menu.options')}
<Settings size="md" />
</button>
</div>
<FieldsSelector
isOpen={isFieldsSelectorOpen}
title="Edit columns"
@@ -48,7 +44,6 @@ function TraceExplorerControls({
onFieldsChange={config.fieldsSelector.onFieldsChange}
onClose={(): void => setIsFieldsSelectorOpen(false)}
signal={DataSource.TRACES}
availableFields={availableFields}
/>
</>
)}
@@ -68,20 +63,20 @@ function TraceExplorerControls({
);
}
TraceExplorerControls.defaultProps = {
config: null,
};
type TraceExplorerControlsProps = Pick<
ControlsProps,
'isLoading' | 'totalCount' | 'perPageOptions'
> & {
config?: OptionsMenuConfig | null;
showSizeChanger?: boolean;
/** Forwarded to FieldsSelector — see `availableFields` there. */
availableFields?: TelemetryFieldKey[];
};
TraceExplorerControls.defaultProps = {
config: null,
showSizeChanger: true,
availableFields: undefined,
};
export default memo(TraceExplorerControls);

View File

@@ -4,105 +4,47 @@ import { Typography } from '@signozhq/ui/typography';
import ROUTES from 'constants/routes';
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
import { DEFAULT_PER_PAGE_OPTIONS } from 'hooks/queryPagination';
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
import { ListItem } from 'types/api/widgets/getQuery';
export const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS];
/**
* One Trace View column: the telemetry field it reads, its header, and how the
* cell renders. Callers own their column sets — Trace View has no knowledge of
* any particular product's columns.
*/
export interface TraceViewColumn {
field: TelemetryFieldKey;
title: string;
/** Defaults to `renderTraceCellValue`. */
render?: (value: unknown) => JSX.Element;
}
function isBlank(value: unknown): boolean {
return value === undefined || value === null || value === '';
}
/** Fallback cell: em dash when empty, otherwise stringified. */
export function renderTraceCellValue(value: unknown): JSX.Element {
if (isBlank(value)) {
return <Typography></Typography>;
}
return <Typography>{String(value)}</Typography>;
}
/** Nanosecond duration rendered as milliseconds. */
export function renderTraceDurationCell(value: unknown): JSX.Element {
if (isBlank(value)) {
return <Typography></Typography>;
}
return <Typography>{getMs(String(value))}ms</Typography>;
}
function renderTraceIdCell(value: unknown): JSX.Element {
if (isBlank(value)) {
return <Typography></Typography>;
}
return (
<Link
to={generatePath(ROUTES.TRACE_DETAIL, {
id: String(value),
})}
data-testid="trace-id"
>
{String(value)}
</Link>
);
}
/**
* The root-span columns Trace View renders when the caller configures no
* column selection. Matches the pre-selection behaviour exactly.
*/
export const BASE_TRACE_VIEW_COLUMNS: TraceViewColumn[] = [
export const columns: ColumnsType<ListItem['data']> = [
{
field: {
name: 'service.name',
fieldContext: 'resource',
fieldDataType: 'string',
},
title: 'Root Service Name',
dataIndex: 'service.name',
key: 'serviceName',
},
{
field: { name: 'name', fieldContext: 'span', fieldDataType: 'string' },
title: 'Root Operation Name',
dataIndex: 'name',
key: 'name',
},
{
field: {
name: 'duration_nano',
fieldContext: 'span',
fieldDataType: 'int64',
},
title: 'Root Duration (in ms)',
render: renderTraceDurationCell,
dataIndex: 'duration_nano',
key: 'durationNano',
render: (duration: number): JSX.Element => (
<Typography>{getMs(String(duration))}ms</Typography>
),
},
{
field: { name: 'span_count', fieldContext: 'trace', fieldDataType: 'int64' },
title: 'No of Spans',
dataIndex: 'span_count',
key: 'span_count',
},
{
field: { name: 'trace_id', fieldContext: 'span', fieldDataType: 'string' },
title: 'TraceID',
render: renderTraceIdCell,
dataIndex: 'trace_id',
key: 'traceID',
render: (traceID: string): JSX.Element => (
<Link
to={generatePath(ROUTES.TRACE_DETAIL, {
id: traceID,
})}
data-testid="trace-id"
>
{traceID}
</Link>
),
},
];
/** Build antd columns, preserving the order given. */
export function buildTraceViewColumns(
columns: TraceViewColumn[],
): ColumnsType<ListItem['data']> {
return columns.map(({ field, title, render }) => ({
title,
dataIndex: field.name,
key: field.name,
render: (value: unknown): JSX.Element =>
(render ?? renderTraceCellValue)(value),
}));
}

View File

@@ -34,26 +34,14 @@ import DOCLINKS from 'utils/docLinks';
import TraceExplorerControls from '../Controls';
import { TracesLoading } from '../TraceLoading/TraceLoading';
import {
BASE_TRACE_VIEW_COLUMNS,
buildTraceViewColumns,
PER_PAGE_OPTIONS,
} from './configs';
import { columns, PER_PAGE_OPTIONS } from './configs';
import { ActionsContainer, Container } from './styles';
import useTraceViewColumns, {
TraceViewColumnSelection,
} from './useTraceViewColumns';
interface TracesViewProps {
isFilterApplied: boolean;
setWarning: Dispatch<SetStateAction<Warning | undefined>>;
setIsLoadingQueries: Dispatch<SetStateAction<boolean>>;
queryKeyRef?: MutableRefObject<any>;
/**
* Opt in to user-editable columns. Omit for the base root-span columns with
* no Options → Edit columns picker.
*/
columnSelection?: TraceViewColumnSelection;
}
function TracesView({
@@ -61,7 +49,6 @@ function TracesView({
setWarning,
setIsLoadingQueries,
queryKeyRef,
columnSelection,
}: TracesViewProps): JSX.Element {
const { stagedQuery, panelType } = useQueryBuilder();
@@ -75,32 +62,6 @@ function TracesView({
QueryParams.pagination,
);
// Column visibility is owned here rather than by useOptionsMenu, whose
// TRACES_LIST_OPTIONS storage is already claimed by List View.
const { visibleColumns, selectedFields, availableFields, onFieldsChange } =
useTraceViewColumns(columnSelection);
const fieldsSelectorConfig = useMemo(
() =>
columnSelection
? {
fieldsSelector: {
value: selectedFields,
onFieldsChange,
},
}
: null,
[columnSelection, selectedFields, onFieldsChange],
);
const tableColumns = useMemo(
() =>
buildTraceViewColumns(
columnSelection ? visibleColumns : BASE_TRACE_VIEW_COLUMNS,
),
[columnSelection, visibleColumns],
);
const transformedQuery = useMemo(
() => getListViewQuery(stagedQuery || initialQueriesMap.traces),
[stagedQuery],
@@ -115,8 +76,6 @@ function TracesView({
stagedQuery,
panelType,
paginationQueryData,
// Column visibility is deliberately absent: it is client-side only, and
// this array doubles as the parent's cancelQueries handle.
],
[
globalSelectedTime,
@@ -141,8 +100,6 @@ function TracesView({
params: {
dataSource: 'traces',
},
// No selectColumns: the backend returns all columns and visibility is
// resolved client-side, so toggling a column is refetch-free.
tableParams: {
pagination: paginationQueryData,
},
@@ -205,8 +162,6 @@ function TracesView({
isLoading={isLoading}
totalCount={responseData?.length || 0}
perPageOptions={PER_PAGE_OPTIONS}
config={fieldsSelectorConfig}
availableFields={columnSelection ? availableFields : undefined}
/>
</div>
</ActionsContainer>
@@ -235,7 +190,7 @@ function TracesView({
{(tableData || []).length !== 0 && (
<ResizeTable
loading={isLoading}
columns={tableColumns}
columns={columns}
tableLayout="fixed"
dataSource={tableData}
scroll={{ x: true }}
@@ -248,7 +203,6 @@ function TracesView({
TracesView.defaultProps = {
queryKeyRef: undefined,
columnSelection: undefined,
};
export default memo(TracesView);

View File

@@ -1,123 +0,0 @@
import { useCallback, useMemo, useState } from 'react';
import getLocalStorageKey from 'api/browser/localstorage/get';
import setLocalStorageKey from 'api/browser/localstorage/set';
import { LOCALSTORAGE } from 'constants/localStorage';
import { TelemetryFieldKey } from 'types/api/v5/queryRange';
import { TraceViewColumn } from './configs';
/**
* Opt-in column selection for Trace View. Passing one enables the Options →
* Edit columns picker; omitting it leaves Trace View on its base columns with
* no picker at all.
*
* Pass a module-level constant, not an inline literal — a fresh object each
* render invalidates the memoised column set on every pass.
*/
export interface TraceViewColumnSelection {
/** Every column offered, in default display order. */
columns: TraceViewColumn[];
/** Where this caller persists visibility. Must be unique per view. */
storageKey: LOCALSTORAGE;
/** Field names visible before the user customises anything. */
defaultVisible: string[];
}
interface UseTraceViewColumnsReturn {
/** Columns to render, in the user's persisted order. */
visibleColumns: TraceViewColumn[];
/** Visible fields, for the picker's "added" list. */
selectedFields: TelemetryFieldKey[];
/** Every offered field, for the picker's "other" list. */
availableFields: TelemetryFieldKey[];
onFieldsChange: (fields: TelemetryFieldKey[]) => void;
}
function readStoredKeys(
selection: TraceViewColumnSelection | undefined,
): string[] {
if (!selection) {
return [];
}
const { storageKey, columns, defaultVisible } = selection;
const raw = getLocalStorageKey(storageKey);
if (!raw) {
return defaultVisible;
}
try {
const parsed = JSON.parse(raw) as string[];
if (!Array.isArray(parsed) || parsed.length === 0) {
return defaultVisible;
}
// Drop anything the caller no longer offers, so a stale localStorage
// entry from an earlier column set self-heals instead of rendering blank.
const allowed = new Set(columns.map((column) => column.field.name));
const filtered = parsed.filter((key) => allowed.has(key));
return filtered.length > 0 ? filtered : defaultVisible;
} catch {
return defaultVisible;
}
}
/**
* Owns Trace View column visibility client-side: toggling reorders or hides
* columns without touching the query, so it never triggers a refetch.
*/
function useTraceViewColumns(
selection?: TraceViewColumnSelection,
): UseTraceViewColumnsReturn {
const [visibleKeys, setVisibleKeys] = useState<string[]>(() =>
readStoredKeys(selection),
);
const offeredColumns = selection?.columns;
const columnsByName = useMemo(
() =>
new Map((offeredColumns ?? []).map((column) => [column.field.name, column])),
[offeredColumns],
);
// Ordered by visibleKeys, so reordering in the picker moves the column.
const visibleColumns = useMemo(
() =>
visibleKeys
.map((key) => columnsByName.get(key))
.filter((column): column is TraceViewColumn => Boolean(column)),
[columnsByName, visibleKeys],
);
const selectedFields = useMemo(
() => visibleColumns.map((column) => column.field),
[visibleColumns],
);
const availableFields = useMemo(
() => (offeredColumns ?? []).map((column) => column.field),
[offeredColumns],
);
const onFieldsChange = useCallback(
(fields: TelemetryFieldKey[]): void => {
if (!selection) {
return;
}
const nextKeys = fields.map((field) => field.name);
const keys = nextKeys.length > 0 ? nextKeys : selection.defaultVisible;
setVisibleKeys(keys);
setLocalStorageKey(selection.storageKey, JSON.stringify(keys));
},
[selection],
);
return {
visibleColumns,
selectedFields,
availableFields,
onFieldsChange,
};
}
export default useTraceViewColumns;

View File

@@ -114,6 +114,23 @@ describe('getCaretContext — stage detection', () => {
expect(ctx.partial).toBe('');
});
it('never replaces past the caret when it sits before the operator', () => {
const ctx = getCaretContext("env = 'prod'", 4);
expect(ctx.stage).toBe('operator');
expect(ctx.partial).toBe('');
expect(ctx.replaceStart).toBe(4);
expect(ctx.replaceEnd).toBe(4);
});
it('never replaces past the caret when it sits before the value', () => {
const ctx = getCaretContext("env = 'prod'", 6);
expect(ctx.stage).toBe('value');
expect(ctx.operator).toBe('=');
expect(ctx.partial).toBe('');
expect(ctx.replaceStart).toBe(6);
expect(ctx.replaceEnd).toBe(6);
});
it('detects the stage of the term under a mid-string caret', () => {
const q = "env = AND team = 'core'";
// caret right after the first `env ` (index 4) is the operator stage
@@ -142,6 +159,13 @@ describe('spliceAtCaret', () => {
expect(next).toBe("env = 'prod'");
});
it('inserts (without duplicating text) at a caret parked before a token', () => {
const q = "env = 'prod'";
const ctx = getCaretContext(q, 4);
const { next } = spliceAtCaret(q, ctx, '!= ');
expect(next).toBe("env != = 'prod'");
});
it('preserves text after the caret', () => {
const q = "env AND team = 'core'";
const ctx = getCaretContext(q, 4); // operator gap after `env`

View File

@@ -328,7 +328,7 @@ export const getCaretContext = (query: string, caret: number): CaretContext => {
fieldKey: scan.key ? scan.key.text : '',
operator: slot.operator,
partial: slot.partial,
replaceStart: term.start + slot.replaceStartRel,
replaceStart: Math.min(term.start + slot.replaceStartRel, pos),
replaceEnd: pos,
};
};

View File

@@ -17,7 +17,6 @@ import { usePageActions } from 'container/AIAssistant/pageActions/usePageActions
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
import { useOptionsMenu } from 'container/OptionsMenu';
import LeftToolbarActions from 'container/QueryBuilder/components/ToolbarActions/LeftToolbarActions';
import { AI_TRACE_VIEW_COLUMN_SELECTION } from 'container/AIObservability/TraceView/aiTraceViewColumns';
import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions';
import Toolbar from 'container/Toolbar/Toolbar';
import {
@@ -331,10 +330,6 @@ function TracesExplorer(): JSX.Element {
setWarning={setWarning}
setIsLoadingQueries={setIsLoadingQueries}
queryKeyRef={listQueryKeyRef}
// DEMO ONLY — surfaces the AI Observability column set here because
// the AI Explorer does not exist yet. Drop this prop (and the import)
// to return Trace View to its five base columns.
columnSelection={AI_TRACE_VIEW_COLUMN_SELECTION}
/>
</div>
)}

View File

@@ -0,0 +1,40 @@
package implcloudintegration
import (
"context"
"testing"
citypes "github.com/SigNoz/signoz/pkg/types/cloudintegrationtypes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestServiceDefinitionsAreValid(t *testing.T) {
store := NewServiceDefinitionStore()
for _, provider := range []citypes.CloudProviderType{
citypes.CloudProviderTypeAWS,
citypes.CloudProviderTypeAzure,
citypes.CloudProviderTypeGCP,
} {
t.Run(provider.StringValue(), func(t *testing.T) {
defs, err := store.List(context.Background(), provider)
require.NoError(t, err, "all embedded definitions must load and validate")
require.NotEmpty(t, defs, "provider should ship at least one service definition")
for _, def := range defs {
assert.NotEmpty(t, def.ID, "service definition must have an id")
assert.NotEmpty(t, def.Title, "service %q must have a title", def.ID)
// Get() must agree with List() for every service it advertises.
serviceID, err := citypes.NewServiceID(provider, def.ID)
if !assert.NoError(t, err, "service id %q must be registered in serviceid.go", def.ID) {
continue
}
got, err := store.Get(context.Background(), provider, serviceID)
require.NoError(t, err, "service %q listed but not gettable", def.ID)
assert.Equal(t, def.ID, got.ID)
}
})
}
}

View File

@@ -621,7 +621,7 @@
{
"metricName": "cloudsql.googleapis.com/database/postgresql/deadlock_count",
"temporality": "",
"timeAggregation": "max",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "avg"
}
@@ -882,7 +882,7 @@
{
"metricName": "cloudsql.googleapis.com/database/postgresql/transaction_count",
"temporality": "",
"timeAggregation": "max",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "avg"
}

View File

@@ -54,13 +54,13 @@
{
"name": "cloudsql.googleapis.com/database/postgresql/transaction_count",
"unit": "Count",
"type": "Gauge",
"type": "Sum",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/deadlock_count",
"unit": "Count",
"type": "Gauge",
"type": "Sum",
"description": ""
},
{

View File

@@ -925,7 +925,7 @@
"metricName": "compute.googleapis.com/instance/disk/average_io_latency",
"temporality": "",
"timeAggregation": "avg",
"spaceAggregation": "sum",
"spaceAggregation": "max",
"reduceTo": "avg"
}
],

View File

@@ -1207,8 +1207,8 @@
{
"metricName": "kubernetes.io/container/restart_count",
"temporality": "",
"timeAggregation": "max",
"spaceAggregation": "max",
"timeAggregation": "increase",
"spaceAggregation": "sum",
"reduceTo": "avg"
}
],

View File

@@ -601,8 +601,8 @@
{
"metricName": "redis.googleapis.com/stats/cache_hit_ratio",
"temporality": "",
"timeAggregation": "max",
"spaceAggregation": "sum",
"timeAggregation": "min",
"spaceAggregation": "min",
"reduceTo": "avg"
}
],
@@ -788,7 +788,7 @@
"metricName": "redis.googleapis.com/commands/usec_per_call",
"temporality": "",
"timeAggregation": "max",
"spaceAggregation": "sum",
"spaceAggregation": "max",
"reduceTo": "avg"
}
],
@@ -945,7 +945,7 @@
{
"metricName": "redis.googleapis.com/commands/calls",
"temporality": "",
"timeAggregation": "max",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "avg"
}
@@ -1044,8 +1044,8 @@
{
"metricName": "redis.googleapis.com/stats/reject_connections_count",
"temporality": "",
"timeAggregation": "avg",
"spaceAggregation": "avg",
"timeAggregation": "rate",
"spaceAggregation": "sum",
"reduceTo": "avg"
}
],
@@ -1213,4 +1213,4 @@
"refreshInterval": "",
"links": []
}
}
}